2 // $Header: /cvs/public/gdcm/src/Attic/gdcmHeader.cxx,v 1.80 2003/07/28 12:21:09 malaterre Exp $
10 #include <netinet/in.h>
12 #include <cctype> // for isalpha
15 #include "gdcmHeader.h"
20 // TODO : remove DEBUG
23 // Refer to gdcmHeader::CheckSwap()
24 #define HEADER_LENGTH_TO_READ 256
25 // Refer to gdcmHeader::SetMaxSizeLoadElementValue()
26 //#define _MaxSizeLoadElementValue_ 1024
27 #define _MaxSizeLoadElementValue_ 4096
32 void gdcmHeader::Initialise(void) {
33 dicom_vr = gdcmGlobal::GetVR();
34 dicom_ts = gdcmGlobal::GetTS();
35 Dicts = gdcmGlobal::GetDicts();
36 RefPubDict = Dicts->GetDefaultPubDict();
37 RefShaDict = (gdcmDict*)0;
44 * @param exception_on_error
46 gdcmHeader::gdcmHeader(const char *InFilename, bool exception_on_error) {
47 SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_);
48 filename = InFilename;
50 if ( !OpenFile(exception_on_error))
60 * @param exception_on_error
62 gdcmHeader::gdcmHeader(bool exception_on_error) {
63 SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_);
70 * @param exception_on_error
73 bool gdcmHeader::OpenFile(bool exception_on_error)
74 throw(gdcmFileError) {
75 fp=fopen(filename.c_str(),"rb");
76 if(exception_on_error) {
78 throw gdcmFileError("gdcmHeader::gdcmHeader(const char *, bool)");
82 dbg.Verbose(0, "gdcmHeader::gdcmHeader cannot open file", filename.c_str());
89 * @return TRUE if the close was successfull
91 bool gdcmHeader::CloseFile(void) {
92 int closed = fclose(fp);
100 * \ingroup gdcmHeader
101 * \brief Canonical destructor.
103 gdcmHeader::~gdcmHeader (void) {
104 dicom_vr = (gdcmVR*)0;
105 Dicts = (gdcmDictSet*)0;
106 RefPubDict = (gdcmDict*)0;
107 RefShaDict = (gdcmDict*)0;
113 // META Meta Information
125 // NMI Nuclear Medicine
127 // BFS Basic Film Session
128 // BFB Basic Film Box
129 // BIB Basic Image Box
145 * \ingroup gdcmHeader
146 * \brief Discover what the swap code is (among little endian, big endian,
147 * bad little endian, bad big endian).
150 void gdcmHeader::CheckSwap()
152 // The only guaranted way of finding the swap code is to find a
153 // group tag since we know it's length has to be of four bytes i.e.
154 // 0x00000004. Finding the swap code in then straigthforward. Trouble
155 // occurs when we can't find such group...
157 guint32 x=4; // x : for ntohs
158 bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
162 char deb[HEADER_LENGTH_TO_READ];
164 // First, compare HostByteOrder and NetworkByteOrder in order to
165 // determine if we shall need to swap bytes (i.e. the Endian type).
171 // The easiest case is the one of a DICOM header, since it possesses a
172 // file preamble where it suffice to look for the string "DICM".
173 lgrLue = fread(deb, 1, HEADER_LENGTH_TO_READ, fp);
176 if(memcmp(entCur, "DICM", (size_t)4) == 0) {
177 dbg.Verbose(1, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
178 // Next, determine the value representation (VR). Let's skip to the
179 // first element (0002, 0000) and check there if we find "UL"
180 // - or "OB" if the 1st one is (0002,0001) -,
181 // in which case we (almost) know it is explicit VR.
182 // WARNING: if it happens to be implicit VR then what we will read
183 // is the length of the group. If this ascii representation of this
184 // length happens to be "UL" then we shall believe it is explicit VR.
185 // FIXME: in order to fix the above warning, we could read the next
186 // element value (or a couple of elements values) in order to make
187 // sure we are not commiting a big mistake.
189 // * the 128 bytes of File Preamble (often padded with zeroes),
190 // * the 4 bytes of "DICM" string,
191 // * the 4 bytes of the first tag (0002, 0000),or (0002, 0001)
192 // i.e. a total of 136 bytes.
195 // Use gdcmHeader::dicom_vr to test all the possibilities
196 // instead of just checking for UL, OB and UI !?
197 if( (memcmp(entCur, "UL", (size_t)2) == 0) ||
198 (memcmp(entCur, "OB", (size_t)2) == 0) ||
199 (memcmp(entCur, "UI", (size_t)2) == 0) )
201 filetype = ExplicitVR;
202 dbg.Verbose(1, "gdcmHeader::CheckSwap:",
203 "explicit Value Representation");
205 filetype = ImplicitVR;
206 dbg.Verbose(1, "gdcmHeader::CheckSwap:",
207 "not an explicit Value Representation");
211 dbg.Verbose(1, "gdcmHeader::CheckSwap:",
212 "HostByteOrder != NetworkByteOrder");
215 dbg.Verbose(1, "gdcmHeader::CheckSwap:",
216 "HostByteOrder = NetworkByteOrder");
219 // Position the file position indicator at first tag (i.e.
220 // after the file preamble and the "DICM" string).
222 fseek (fp, 132L, SEEK_SET);
226 // Alas, this is not a DicomV3 file and whatever happens there is no file
227 // preamble. We can reset the file position indicator to where the data
228 // is (i.e. the beginning of the file).
229 dbg.Verbose(1, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
232 // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
233 // By clean we mean that the length of the first tag is written down.
234 // If this is the case and since the length of the first group HAS to be
235 // four (bytes), then determining the proper swap code is straightforward.
238 // We assume the array of char we are considering contains the binary
239 // representation of a 32 bits integer. Hence the following dirty
241 s = *((guint32 *)(entCur));
261 dbg.Verbose(0, "gdcmHeader::CheckSwap:",
262 "ACR/NEMA unfound swap info (time to raise bets)");
265 // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
266 // It is time for despaired wild guesses. So, let's assume this file
267 // happens to be 'dirty' ACR/NEMA, i.e. the length of the group is
268 // not present. Then the only info we have is the net2host one.
278 * \ingroup gdcmHeader
281 void gdcmHeader::SwitchSwapToBigEndian(void) {
282 dbg.Verbose(1, "gdcmHeader::SwitchSwapToBigEndian",
283 "Switching to BigEndian mode.");
301 * \ingroup gdcmHeader
302 * \brief Find the value representation of the current tag.
305 void gdcmHeader::FindVR( gdcmElValue *ElVal) {
306 if (filetype != ExplicitVR)
312 char msg[100]; // for sprintf. Sorry
314 long PositionOnEntry = ftell(fp);
315 // Warning: we believe this is explicit VR (Value Representation) because
316 // we used a heuristic that found "UL" in the first tag. Alas this
317 // doesn't guarantee that all the tags will be in explicit VR. In some
318 // cases (see e-film filtered files) one finds implicit VR tags mixed
319 // within an explicit VR file. Hence we make sure the present tag
320 // is in explicit VR and try to fix things if it happens not to be
322 bool RealExplicit = true;
324 lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
328 // Assume we are reading a falsely explicit VR file i.e. we reached
329 // a tag where we expect reading a VR but are in fact we read the
330 // first to bytes of the length. Then we will interogate (through find)
331 // the dicom_vr dictionary with oddities like "\004\0" which crashes
332 // both GCC and VC++ implementations of the STL map. Hence when the
333 // expected VR read happens to be non-ascii characters we consider
334 // we hit falsely explicit VR tag.
336 if ( (!isalpha(VR[0])) && (!isalpha(VR[1])) )
337 RealExplicit = false;
339 // CLEANME searching the dicom_vr at each occurence is expensive.
340 // PostPone this test in an optional integrity check at the end
341 // of parsing or only in debug mode.
342 if ( RealExplicit && !dicom_vr->Count(vr) )
345 if ( RealExplicit ) {
346 if ( ElVal->IsVrUnknown() ) {
347 // When not a dictionary entry, we can safely overwrite the vr.
351 if ( ElVal->GetVR() == vr ) {
352 // The vr we just read and the dictionary agree. Nothing to do.
355 // The vr present in the file and the dictionary disagree. We assume
356 // the file writer knew best and use the vr of the file. Since it would
357 // be unwise to overwrite the vr of a dictionary (since it would
358 // compromise it's next user), we need to clone the actual DictEntry
359 // and change the vr for the read one.
360 gdcmDictEntry* NewTag = new gdcmDictEntry(ElVal->GetGroup(),
365 ElVal->SetDictEntry(NewTag);
369 // We thought this was explicit VR, but we end up with an
370 // implicit VR tag. Let's backtrack.
372 sprintf(msg,"Falsely explicit vr file (%04x,%04x)\n", ElVal->GetGroup(),ElVal->GetElement());
373 dbg.Verbose(1, "gdcmHeader::FindVR: ",msg);
375 fseek(fp, PositionOnEntry, SEEK_SET);
376 // When this element is known in the dictionary we shall use, e.g. for
377 // the semantics (see the usage of IsAnInteger), the vr proposed by the
378 // dictionary entry. Still we have to flag the element as implicit since
379 // we know now our assumption on expliciteness is not furfilled.
381 if ( ElVal->IsVrUnknown() )
382 ElVal->SetVR("Implicit");
383 ElVal->SetImplicitVr();
387 * \ingroup gdcmHeader
388 * \brief Determines if the Transfer Syntax was already encountered
389 * and if it corresponds to a ImplicitVRLittleEndian one.
391 * @return True when ImplicitVRLittleEndian found. False in all other cases.
393 bool gdcmHeader::IsImplicitVRLittleEndianTransferSyntax(void) {
394 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
397 LoadElementValueSafe(Element);
398 string Transfer = Element->GetValue();
399 if ( Transfer == "1.2.840.10008.1.2" )
405 * \ingroup gdcmHeader
406 * \brief Determines if the Transfer Syntax was already encountered
407 * and if it corresponds to a ExplicitVRLittleEndian one.
409 * @return True when ExplicitVRLittleEndian found. False in all other cases.
411 bool gdcmHeader::IsExplicitVRLittleEndianTransferSyntax(void) {
412 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
415 LoadElementValueSafe(Element);
416 string Transfer = Element->GetValue();
417 if ( Transfer == "1.2.840.10008.1.2.1" )
423 * \ingroup gdcmHeader
424 * \brief Determines if the Transfer Syntax was already encountered
425 * and if it corresponds to a DeflatedExplicitVRLittleEndian one.
427 * @return True when DeflatedExplicitVRLittleEndian found. False in all other cases.
429 bool gdcmHeader::IsDeflatedExplicitVRLittleEndianTransferSyntax(void) {
430 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
433 LoadElementValueSafe(Element);
434 string Transfer = Element->GetValue();
435 if ( Transfer == "1.2.840.10008.1.2.1.99" )
441 * \ingroup gdcmHeader
442 * \brief Determines if the Transfer Syntax was already encountered
443 * and if it corresponds to a Explicit VR Big Endian one.
445 * @return True when big endian found. False in all other cases.
447 bool gdcmHeader::IsExplicitVRBigEndianTransferSyntax(void) {
448 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
451 LoadElementValueSafe(Element);
452 string Transfer = Element->GetValue();
453 if ( Transfer == "1.2.840.10008.1.2.2" ) //1.2.2 ??? A verifier !
459 * \ingroup gdcmHeader
460 * \brief Determines if the Transfer Syntax was already encountered
461 * and if it corresponds to a JPEGBaseLineProcess1 one.
463 * @return True when JPEGBaseLineProcess1found. False in all other cases.
465 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
466 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
469 LoadElementValueSafe(Element);
470 string Transfer = Element->GetValue();
471 if ( Transfer == "1.2.840.10008.1.2.4.50" )
477 * \ingroup gdcmHeader
482 bool gdcmHeader::IsJPEGLossless(void) {
483 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
484 // faire qq chose d'intelligent a la place de ça
487 LoadElementValueSafe(Element);
488 const char * Transfert = Element->GetValue().c_str();
489 if ( memcmp(Transfert+strlen(Transfert)-2 ,"70",2)==0) return true;
490 if ( memcmp(Transfert+strlen(Transfert)-2 ,"55",2)==0) return true;
496 * \ingroup gdcmHeader
497 * \brief Determines if the Transfer Syntax was already encountered
498 * and if it corresponds to a JPEGExtendedProcess2-4 one.
500 * @return True when JPEGExtendedProcess2-4 found. False in all other cases.
502 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
503 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
506 LoadElementValueSafe(Element);
507 string Transfer = Element->GetValue();
508 if ( Transfer == "1.2.840.10008.1.2.4.51" )
514 * \ingroup gdcmHeader
515 * \brief Determines if the Transfer Syntax was already encountered
516 * and if it corresponds to a JPEGExtendeProcess3-5 one.
518 * @return True when JPEGExtendedProcess3-5 found. False in all other cases.
520 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
521 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
524 LoadElementValueSafe(Element);
525 string Transfer = Element->GetValue();
526 if ( Transfer == "1.2.840.10008.1.2.4.52" )
532 * \ingroup gdcmHeader
533 * \brief Determines if the Transfer Syntax was already encountered
534 * and if it corresponds to a JPEGSpectralSelectionProcess6-8 one.
536 * @return True when JPEGSpectralSelectionProcess6-8 found. False in all
539 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
540 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
543 LoadElementValueSafe(Element);
544 string Transfer = Element->GetValue();
545 if ( Transfer == "1.2.840.10008.1.2.4.53" )
551 * \ingroup gdcmHeader
552 * \brief Determines if the Transfer Syntax was already encountered
553 * and if it corresponds to a RLE Lossless one.
555 * @return True when RLE Lossless found. False in all
558 bool gdcmHeader::IsRLELossLessTransferSyntax(void) {
559 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
562 LoadElementValueSafe(Element);
563 string Transfer = Element->GetValue();
564 if ( Transfer == "1.2.840.10008.1.2.5" )
570 * \ingroup gdcmHeader
571 * \brief Determines if the Transfer Syntax was already encountered
572 * and if it corresponds to a JPEG200 one.0
574 * @return True when JPEG2000 (Lossly or LossLess) found. False in all
577 bool gdcmHeader::IsJPEG2000(void) {
578 gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
581 LoadElementValueSafe(Element);
582 string Transfer = Element->GetValue();
583 if ( (Transfer == "1.2.840.10008.1.2.4.90")
584 || (Transfer == "1.2.840.10008.1.2.4.91") )
590 * \ingroup gdcmHeader
591 * \brief Predicate for dicom version 3 file.
592 * @return True when the file is a dicom version 3.
594 bool gdcmHeader::IsDicomV3(void) {
595 if ( (filetype == ExplicitVR)
596 || (filetype == ImplicitVR) )
602 * \ingroup gdcmHeader
603 * \brief When the length of an element value is obviously wrong (because
604 * the parser went Jabberwocky) one can hope improving things by
605 * applying this heuristic.
607 void gdcmHeader::FixFoundLength(gdcmElValue * ElVal, guint32 FoundLength) {
608 if ( FoundLength == 0xffffffff)
610 ElVal->SetLength(FoundLength);
614 * \ingroup gdcmHeader
619 guint32 gdcmHeader::FindLengthOB(void) {
620 // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
623 long PositionOnEntry = ftell(fp);
624 bool FoundSequenceDelimiter = false;
625 guint32 TotalLength = 0;
628 while ( ! FoundSequenceDelimiter) {
636 TotalLength += 4; // We even have to decount the group and element
638 if ( g != 0xfffe && g!=0xb00c ) /*for bogus header */ {
639 char msg[100]; // for sprintf. Sorry
640 sprintf(msg,"wrong group (%04x) for an item sequence (%04x,%04x)\n",g, g,n);
641 dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",msg);
646 if ( n == 0xe0dd || ( g==0xb00c && n==0x0eb6 ) ) /* for bogus header */
647 FoundSequenceDelimiter = true;
648 else if ( n != 0xe000 ){
649 char msg[100]; // for sprintf. Sorry
650 sprintf(msg,"wrong element (%04x) for an item sequence (%04x,%04x)\n",n, g,n);
651 dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",msg);
655 ItemLength = ReadInt32();
656 TotalLength += ItemLength + 4; // We add 4 bytes since we just read
657 // the ItemLength with ReadInt32
659 SkipBytes(ItemLength);
661 fseek(fp, PositionOnEntry, SEEK_SET);
666 * \ingroup gdcmHeader
671 void gdcmHeader::FindLength (gdcmElValue * ElVal) {
672 guint16 element = ElVal->GetElement();
673 guint16 group = ElVal->GetGroup();
674 string vr = ElVal->GetVR();
676 if( (element == 0x0010) && (group == 0x7fe0) ) {
678 dbg.Verbose(2, "gdcmHeader::FindLength: ",
679 "on est sur 7fe0 0010");
682 if ( (filetype == ExplicitVR) && ! ElVal->IsImplicitVr() ) {
683 if ( (vr=="OB") || (vr=="OW") || (vr=="SQ") || (vr=="UN") ) {
685 // The following reserved two bytes (see PS 3.5-2001, section
686 // 7.1.2 Data element structure with explicit vr p27) must be
687 // skipped before proceeding on reading the length on 4 bytes.
688 fseek(fp, 2L, SEEK_CUR);
690 guint32 length32 = ReadInt32();
691 if ( (vr == "OB") && (length32 == 0xffffffff) ) {
692 ElVal->SetLength(FindLengthOB());
695 FixFoundLength(ElVal, length32);
699 // Length is encoded on 2 bytes.
700 length16 = ReadInt16();
702 // We can tell the current file is encoded in big endian (like
703 // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
704 // and it's value is the one of the encoding of a big endian file.
705 // In order to deal with such big endian encoded files, we have
706 // (at least) two strategies:
707 // * when we load the "Transfer Syntax" tag with value of big endian
708 // encoding, we raise the proper flags. Then we wait for the end
709 // of the META group (0x0002) among which is "Transfer Syntax",
710 // before switching the swap code to big endian. We have to postpone
711 // the switching of the swap code since the META group is fully encoded
712 // in little endian, and big endian coding only starts at the next
713 // group. The corresponding code can be hard to analyse and adds
714 // many additional unnecessary tests for regular tags.
715 // * the second strategy consists in waiting for trouble, that shall
716 // appear when we find the first group with big endian encoding. This
717 // is easy to detect since the length of a "Group Length" tag (the
718 // ones with zero as element number) has to be of 4 (0x0004). When we
719 // encouter 1024 (0x0400) chances are the encoding changed and we
720 // found a group with big endian encoding.
721 // We shall use this second strategy. In order to make sure that we
722 // can interpret the presence of an apparently big endian encoded
723 // length of a "Group Length" without committing a big mistake, we
724 // add an additional check: we look in the already parsed elements
725 // for the presence of a "Transfer Syntax" whose value has to be "big
726 // endian encoding". When this is the case, chances are we have got our
727 // hands on a big endian encoded file: we switch the swap code to
728 // big endian and proceed...
729 if ( (element == 0x0000) && (length16 == 0x0400) ) {
730 if ( ! IsExplicitVRBigEndianTransferSyntax() ) {
731 dbg.Verbose(0, "gdcmHeader::FindLength", "not explicit VR");
736 SwitchSwapToBigEndian();
737 // Restore the unproperly loaded values i.e. the group, the element
738 // and the dictionary entry depending on them.
739 guint16 CorrectGroup = SwapShort(ElVal->GetGroup());
740 guint16 CorrectElem = SwapShort(ElVal->GetElement());
741 gdcmDictEntry * NewTag = GetDictEntryByNumber(CorrectGroup,
744 // This correct tag is not in the dictionary. Create a new one.
745 NewTag = new gdcmDictEntry(CorrectGroup, CorrectElem);
747 // FIXME this can create a memory leaks on the old entry that be
748 // left unreferenced.
749 ElVal->SetDictEntry(NewTag);
752 // Heuristic: well some files are really ill-formed.
753 if ( length16 == 0xffff) {
755 dbg.Verbose(0, "gdcmHeader::FindLength",
756 "Erroneous element length fixed.");
758 FixFoundLength(ElVal, (guint32)length16);
762 // Either implicit VR or a non DICOM conformal (see not below) explicit
763 // VR that ommited the VR of (at least) this element. Farts happen.
764 // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
765 // on Data elements "Implicit and Explicit VR Data Elements shall
766 // not coexist in a Data Set and Data Sets nested within it".]
767 // Length is on 4 bytes.
768 FixFoundLength(ElVal, ReadInt32());
772 * \ingroup gdcmHeader
773 * \brief Swaps back the bytes of 4-byte long integer accordingly to
776 * @return The suggested integer.
778 guint32 gdcmHeader::SwapLong(guint32 a) {
783 a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) |
784 ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) );
788 a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
792 a=( ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) );
795 dbg.Error(" gdcmHeader::SwapLong : unset swap code");
802 * \ingroup gdcmHeader
803 * \brief Swaps the bytes so they agree with the processor order
804 * @return The properly swaped 16 bits integer.
806 guint16 gdcmHeader::SwapShort(guint16 a) {
807 if ( (sw==4321) || (sw==2143) )
808 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
813 * \ingroup gdcmHeader
818 void gdcmHeader::SkipBytes(guint32 NBytes) {
819 //FIXME don't dump the returned value
820 (void)fseek(fp, (long)NBytes, SEEK_CUR);
824 * \ingroup gdcmHeader
829 void gdcmHeader::SkipElementValue(gdcmElValue * ElVal) {
830 SkipBytes(ElVal->GetLength());
834 * \ingroup gdcmHeader
839 void gdcmHeader::SetMaxSizeLoadElementValue(long NewSize) {
842 if ((guint32)NewSize >= (guint32)0xffffffff) {
843 MaxSizeLoadElementValue = 0xffffffff;
846 MaxSizeLoadElementValue = NewSize;
850 * \ingroup gdcmHeader
851 * \brief Loads the element content if it's length is not bigger
852 * than the value specified with
853 * gdcmHeader::SetMaxSizeLoadElementValue()
855 void gdcmHeader::LoadElementValue(gdcmElValue * ElVal) {
857 guint16 group = ElVal->GetGroup();
858 string vr = ElVal->GetVR();
859 guint32 length = ElVal->GetLength();
860 bool SkipLoad = false;
862 fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
864 // FIXME Sequences not treated yet !
866 // Ne faudrait-il pas au contraire trouver immediatement
867 // une maniere 'propre' de traiter les sequences (vr = SQ)
868 // car commencer par les ignorer risque de conduire a qq chose
869 // qui pourrait ne pas etre generalisable
870 // Well, I'm expecting your code !!!
875 // Heuristic : a sequence "contains" a set of tags (called items). It looks
876 // like the last tag of a sequence (the one that terminates the sequence)
877 // has a group of 0xfffe (with a dummy length).
879 // Actually (fffe e000) tells us an Element is beginning
880 // (fffe e00d) tells us an Element just ended
881 // (fffe e0dd) tells us the current SEQuence just ended
883 if( group == 0xfffe )
888 ElVal->SetValue("gdcm::Skipped");
892 // When the length is zero things are easy:
898 // The elements whose length is bigger than the specified upper bound
899 // are not loaded. Instead we leave a short notice of the offset of
900 // the element content and it's length.
901 if (length > MaxSizeLoadElementValue) {
903 s << "gdcm::NotLoaded.";
904 s << " Address:" << (long)ElVal->GetOffset();
905 s << " Length:" << ElVal->GetLength();
906 ElVal->SetValue(s.str());
910 // When an integer is expected, read and convert the following two or
911 // four bytes properly i.e. as an integer as opposed to a string.
913 // pour les elements de Value Multiplicity > 1
914 // on aura en fait une serie d'entiers
915 // on devrait pouvoir faire + compact (?)
917 if ( IsAnInteger(ElVal) ) {
921 if (vr == "US" || vr == "SS") {
923 NewInt = ReadInt16();
926 for (int i=1; i < nbInt; i++) {
928 NewInt = ReadInt16();
933 } else if (vr == "UL" || vr == "SL") {
935 NewInt = ReadInt32();
938 for (int i=1; i < nbInt; i++) {
940 NewInt = ReadInt32();
945 ElVal->SetValue(s.str());
949 // We need an additional byte for storing \0 that is not on disk
950 char* NewValue = (char*)malloc(length+1);
952 dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
957 item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
958 if ( item_read != 1 ) {
960 dbg.Verbose(1, "gdcmHeader::LoadElementValue","unread element value");
961 ElVal->SetValue("gdcm::UnRead");
964 ElVal->SetValue(NewValue);
969 * \ingroup gdcmHeader
970 * \brief Loads the element while preserving the current
971 * underlying file position indicator as opposed to
972 * to LoadElementValue that modifies it.
973 * @param ElVal Element whose value shall be loaded.
976 void gdcmHeader::LoadElementValueSafe(gdcmElValue * ElVal) {
977 long PositionOnEntry = ftell(fp);
978 LoadElementValue(ElVal);
979 fseek(fp, PositionOnEntry, SEEK_SET);
983 * \ingroup gdcmHeader
984 * \brief Reads a supposed to be 16 Bits integer
985 * \ (swaps it depending on processor endianity)
987 * @return integer acts as a boolean
989 guint16 gdcmHeader::ReadInt16(void) {
992 item_read = fread (&g, (size_t)2,(size_t)1, fp);
993 if ( item_read != 1 ) {
994 // dbg.Verbose(0, "gdcmHeader::ReadInt16", " Failed to read :");
996 // dbg.Verbose(0, "gdcmHeader::ReadInt16", " End of File encountered");
998 dbg.Verbose(0, "gdcmHeader::ReadInt16", " File Error");
1008 * \ingroup gdcmHeader
1009 * \brief Reads a supposed to be 32 Bits integer
1010 * \ (swaps it depending on processor endianity)
1014 guint32 gdcmHeader::ReadInt32(void) {
1017 item_read = fread (&g, (size_t)4,(size_t)1, fp);
1018 if ( item_read != 1 ) {
1019 //dbg.Verbose(0, "gdcmHeader::ReadInt32", " Failed to read :");
1021 // dbg.Verbose(0, "gdcmHeader::ReadInt32", " End of File encountered");
1023 dbg.Verbose(0, "gdcmHeader::ReadInt32", " File Error");
1033 * \ingroup gdcmHeader
1038 gdcmElValue* gdcmHeader::GetElValueByNumber(guint16 Group, guint16 Elem) {
1040 gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem);
1042 dbg.Verbose(1, "gdcmHeader::GetElValueByNumber",
1043 "failed to Locate gdcmElValue");
1044 return (gdcmElValue*)0;
1050 * \ingroup gdcmHeader
1051 * \brief Build a new Element Value from all the low level arguments.
1052 * Check for existence of dictionary entry, and build
1053 * a default one when absent.
1054 * @param Group group of the underlying DictEntry
1055 * @param Elem element of the underlying DictEntry
1057 gdcmElValue* gdcmHeader::NewElValueByNumber(guint16 Group, guint16 Elem) {
1058 // Find out if the tag we encountered is in the dictionaries:
1059 gdcmDictEntry * NewTag = GetDictEntryByNumber(Group, Elem);
1061 NewTag = new gdcmDictEntry(Group, Elem);
1063 gdcmElValue* NewElVal = new gdcmElValue(NewTag);
1065 dbg.Verbose(1, "gdcmHeader::NewElValueByNumber",
1066 "failed to allocate gdcmElValue");
1067 return (gdcmElValue*)0;
1073 * \ingroup gdcmHeader
1078 * \return integer acts as a boolean
1080 int gdcmHeader::ReplaceOrCreateByNumber(string Value, guint16 Group, guint16 Elem ) {
1082 // TODO : FIXME JPRx
1084 // on (je) cree une Elvalue ne contenant pas de valeur
1085 // on l'ajoute au ElValSet
1086 // on affecte une valeur a cette ElValue a l'interieur du ElValSet
1087 // --> devrait pouvoir etre fait + simplement ???
1089 gdcmElValue* nvElValue=NewElValueByNumber(Group, Elem);
1090 PubElValSet.Add(nvElValue);
1091 PubElValSet.SetElValueByNumber(Value, Group, Elem);
1097 * \ingroup gdcmHeader
1098 * \brief Modify or (Creates if not found) an element
1099 * @param Value new value
1102 * \return integer acts as a boolean
1105 int gdcmHeader::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) {
1107 gdcmElValue* nvElValue=NewElValueByNumber(Group, Elem);
1108 PubElValSet.Add(nvElValue);
1110 PubElValSet.SetElValueByNumber(v, Group, Elem);
1116 * \ingroup gdcmHeader
1117 * \brief Set a new value if the invoked element exists
1121 * \return integer acts as a boolean
1123 int gdcmHeader::ReplaceIfExistByNumber(char* Value, guint16 Group, guint16 Elem ) {
1125 gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem);
1127 PubElValSet.SetElValueByNumber(v, Group, Elem);
1133 * \ingroup gdcmHeader
1134 * \brief Checks if a given ElValue (group,number)
1135 * \ exists in the Public ElValSet
1138 * @return integer acts as a boolean
1141 int gdcmHeader::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
1142 return (PubElValSet.CheckIfExistByNumber(Group, Elem));
1146 * \ingroup gdcmHeader
1147 * \brief Build a new Element Value from all the low level arguments.
1148 * Check for existence of dictionary entry, and build
1149 * a default one when absent.
1150 * @param Name Name of the underlying DictEntry
1152 gdcmElValue* gdcmHeader::NewElValueByName(string Name) {
1154 gdcmDictEntry * NewTag = GetDictEntryByName(Name);
1156 NewTag = new gdcmDictEntry(0xffff, 0xffff, "LO", "Unknown", Name);
1158 gdcmElValue* NewElVal = new gdcmElValue(NewTag);
1160 dbg.Verbose(1, "gdcmHeader::ObtainElValueByName",
1161 "failed to allocate gdcmElValue");
1162 return (gdcmElValue*)0;
1168 * \ingroup gdcmHeader
1169 * \brief Read the next tag but WITHOUT loading it's value
1170 * @return On succes the newly created ElValue, NULL on failure.
1172 gdcmElValue * gdcmHeader::ReadNextElement(void) {
1175 gdcmElValue * NewElVal;
1180 if ( (g==0x7fe0) && (n==0x0010) )
1182 printf("in gdcmHeader::ReadNextElement try to read 7fe0 0010 \n");
1185 // We reached the EOF (or an error occured) and header parsing
1186 // has to be considered as finished.
1187 return (gdcmElValue *)0;
1189 NewElVal = NewElValueByNumber(g, n);
1191 FindLength(NewElVal);
1194 if (DEBUG) printf("in gdcmHeader::ReadNextElement : g %04x n %04x errno %d\n",g, n, errno);
1195 return (gdcmElValue *)0;
1197 NewElVal->SetOffset(ftell(fp));
1198 if ( (g==0x7fe0) && (n==0x0010) )
1200 printf("sortie de gdcmHeader::ReadNextElement 7fe0 0010 \n");
1205 * \ingroup gdcmHeader
1206 * \brief Apply some heuristics to predict wether the considered
1207 * element value contains/represents an integer or not.
1208 * @param ElVal The element value on which to apply the predicate.
1209 * @return The result of the heuristical predicate.
1211 bool gdcmHeader::IsAnInteger(gdcmElValue * ElVal) {
1212 guint16 group = ElVal->GetGroup();
1213 guint16 element = ElVal->GetElement();
1214 string vr = ElVal->GetVR();
1215 guint32 length = ElVal->GetLength();
1217 // When we have some semantics on the element we just read, and if we
1218 // a priori know we are dealing with an integer, then we shall be
1219 // able to swap it's element value properly.
1220 if ( element == 0 ) { // This is the group length of the group
1224 if (DEBUG) printf("Erroneous Group Length element length (%04x , %04x) : %d\n",
1225 group, element,length);
1227 dbg.Error("gdcmHeader::IsAnInteger",
1228 "Erroneous Group Length element length.");
1231 if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
1238 * \ingroup gdcmHeader
1239 * \brief Recover the offset (from the beginning of the file) of the pixels.
1241 size_t gdcmHeader::GetPixelOffset(void) {
1242 // If this file complies with the norm we should encounter the
1243 // "Image Location" tag (0x0028, 0x0200). This tag contains the
1244 // the group that contains the pixel data (hence the "Pixel Data"
1245 // is found by indirection through the "Image Location").
1246 // Inside the group pointed by "Image Location" the searched element
1247 // is conventionally the element 0x0010 (when the norm is respected).
1248 // When the "Image Location" is absent we default to group 0x7fe0.
1251 string ImageLocation = GetPubElValByName("Image Location");
1252 if ( ImageLocation == "gdcm::Unfound" ) {
1255 grPixel = (guint16) atoi( ImageLocation.c_str() );
1257 if (grPixel != 0x7fe0)
1258 // This is a kludge for old dirty Philips imager.
1263 gdcmElValue* PixelElement = PubElValSet.GetElementByNumber(grPixel,
1266 return PixelElement->GetOffset();
1272 * \ingroup gdcmHeader
1273 * \brief Searches both the public and the shadow dictionary (when they
1274 * exist) for the presence of the DictEntry with given
1275 * group and element. The public dictionary has precedence on the
1277 * @param group group of the searched DictEntry
1278 * @param element element of the searched DictEntry
1279 * @return Corresponding DictEntry when it exists, NULL otherwise.
1281 gdcmDictEntry * gdcmHeader::GetDictEntryByNumber(guint16 group,
1283 gdcmDictEntry * found = (gdcmDictEntry*)0;
1284 if (!RefPubDict && !RefShaDict) {
1285 dbg.Verbose(0, "gdcmHeader::GetDictEntry",
1286 "we SHOULD have a default dictionary");
1289 found = RefPubDict->GetTagByNumber(group, element);
1294 found = RefShaDict->GetTagByNumber(group, element);
1302 * \ingroup gdcmHeader
1303 * \brief Searches both the public and the shadow dictionary (when they
1304 * exist) for the presence of the DictEntry with given name.
1305 * The public dictionary has precedence on the shadow one.
1306 * @param Name name of the searched DictEntry
1307 * @return Corresponding DictEntry when it exists, NULL otherwise.
1309 gdcmDictEntry * gdcmHeader::GetDictEntryByName(string Name) {
1310 gdcmDictEntry * found = (gdcmDictEntry*)0;
1311 if (!RefPubDict && !RefShaDict) {
1312 dbg.Verbose(0, "gdcmHeader::GetDictEntry",
1313 "we SHOULD have a default dictionary");
1316 found = RefPubDict->GetTagByName(Name);
1321 found = RefShaDict->GetTagByName(Name);
1329 * \ingroup gdcmHeader
1330 * \brief Searches within the public dictionary for element value of
1332 * @param group Group of the researched tag.
1333 * @param element Element of the researched tag.
1334 * @return Corresponding element value when it exists, and the string
1335 * "gdcm::Unfound" otherwise.
1337 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
1338 return PubElValSet.GetElValueByNumber(group, element);
1342 * \ingroup gdcmHeader
1343 * \brief Searches within the public dictionary for element value
1344 * representation of a given tag.
1346 * Obtaining the VR (Value Representation) might be needed by caller
1347 * to convert the string typed content to caller's native type
1348 * (think of C++ vs Python). The VR is actually of a higher level
1349 * of semantics than just the native C++ type.
1350 * @param group Group of the researched tag.
1351 * @param element Element of the researched tag.
1352 * @return Corresponding element value representation when it exists,
1353 * and the string "gdcm::Unfound" otherwise.
1355 string gdcmHeader::GetPubElValRepByNumber(guint16 group, guint16 element) {
1356 gdcmElValue* elem = PubElValSet.GetElementByNumber(group, element);
1358 return "gdcm::Unfound";
1359 return elem->GetVR();
1363 * \ingroup gdcmHeader
1364 * \brief Searches within the public dictionary for element value of
1366 * @param TagName name of the researched element.
1367 * @return Corresponding element value when it exists, and the string
1368 * "gdcm::Unfound" otherwise.
1370 string gdcmHeader::GetPubElValByName(string TagName) {
1371 return PubElValSet.GetElValueByName(TagName);
1375 * \ingroup gdcmHeader
1376 * \brief Searches within the elements parsed with the public dictionary for
1377 * the element value representation of a given tag.
1379 * Obtaining the VR (Value Representation) might be needed by caller
1380 * to convert the string typed content to caller's native type
1381 * (think of C++ vs Python). The VR is actually of a higher level
1382 * of semantics than just the native C++ type.
1383 * @param TagName name of the researched element.
1384 * @return Corresponding element value representation when it exists,
1385 * and the string "gdcm::Unfound" otherwise.
1387 string gdcmHeader::GetPubElValRepByName(string TagName) {
1388 gdcmElValue* elem = PubElValSet.GetElementByName(TagName);
1390 return "gdcm::Unfound";
1391 return elem->GetVR();
1395 * \ingroup gdcmHeader
1396 * \brief Searches within elements parsed with the SHADOW dictionary
1397 * for the element value of a given tag.
1398 * @param group Group of the researched tag.
1399 * @param element Element of the researched tag.
1400 * @return Corresponding element value representation when it exists,
1401 * and the string "gdcm::Unfound" otherwise.
1403 string gdcmHeader::GetShaElValByNumber(guint16 group, guint16 element) {
1404 return ShaElValSet.GetElValueByNumber(group, element);
1408 * \ingroup gdcmHeader
1409 * \brief Searches within the elements parsed with the SHADOW dictionary
1410 * for the element value representation of a given tag.
1412 * Obtaining the VR (Value Representation) might be needed by caller
1413 * to convert the string typed content to caller's native type
1414 * (think of C++ vs Python). The VR is actually of a higher level
1415 * of semantics than just the native C++ type.
1416 * @param group Group of the researched tag.
1417 * @param element Element of the researched tag.
1418 * @return Corresponding element value representation when it exists,
1419 * and the string "gdcm::Unfound" otherwise.
1421 string gdcmHeader::GetShaElValRepByNumber(guint16 group, guint16 element) {
1422 gdcmElValue* elem = ShaElValSet.GetElementByNumber(group, element);
1424 return "gdcm::Unfound";
1425 return elem->GetVR();
1429 * \ingroup gdcmHeader
1430 * \brief Searches within the elements parsed with the shadow dictionary
1431 * for an element value of given tag.
1432 * @param TagName name of the researched element.
1433 * @return Corresponding element value when it exists, and the string
1434 * "gdcm::Unfound" otherwise.
1436 string gdcmHeader::GetShaElValByName(string TagName) {
1437 return ShaElValSet.GetElValueByName(TagName);
1441 * \ingroup gdcmHeader
1442 * \brief Searches within the elements parsed with the shadow dictionary for
1443 * the element value representation of a given tag.
1445 * Obtaining the VR (Value Representation) might be needed by caller
1446 * to convert the string typed content to caller's native type
1447 * (think of C++ vs Python). The VR is actually of a higher level
1448 * of semantics than just the native C++ type.
1449 * @param TagName name of the researched element.
1450 * @return Corresponding element value representation when it exists,
1451 * and the string "gdcm::Unfound" otherwise.
1453 string gdcmHeader::GetShaElValRepByName(string TagName) {
1454 gdcmElValue* elem = ShaElValSet.GetElementByName(TagName);
1456 return "gdcm::Unfound";
1457 return elem->GetVR();
1461 * \ingroup gdcmHeader
1462 * \brief Searches within elements parsed with the public dictionary
1463 * and then within the elements parsed with the shadow dictionary
1464 * for the element value of a given tag.
1465 * @param group Group of the researched tag.
1466 * @param element Element of the researched tag.
1467 * @return Corresponding element value representation when it exists,
1468 * and the string "gdcm::Unfound" otherwise.
1470 string gdcmHeader::GetElValByNumber(guint16 group, guint16 element) {
1471 string pub = GetPubElValByNumber(group, element);
1474 return GetShaElValByNumber(group, element);
1478 * \ingroup gdcmHeader
1479 * \brief Searches within elements parsed with the public dictionary
1480 * and then within the elements parsed with the shadow dictionary
1481 * for the element value representation of a given tag.
1483 * Obtaining the VR (Value Representation) might be needed by caller
1484 * to convert the string typed content to caller's native type
1485 * (think of C++ vs Python). The VR is actually of a higher level
1486 * of semantics than just the native C++ type.
1487 * @param group Group of the researched tag.
1488 * @param element Element of the researched tag.
1489 * @return Corresponding element value representation when it exists,
1490 * and the string "gdcm::Unfound" otherwise.
1492 string gdcmHeader::GetElValRepByNumber(guint16 group, guint16 element) {
1493 string pub = GetPubElValRepByNumber(group, element);
1496 return GetShaElValRepByNumber(group, element);
1500 * \ingroup gdcmHeader
1501 * \brief Searches within elements parsed with the public dictionary
1502 * and then within the elements parsed with the shadow dictionary
1503 * for the element value of a given tag.
1504 * @param TagName name of the researched element.
1505 * @return Corresponding element value when it exists,
1506 * and the string "gdcm::Unfound" otherwise.
1508 string gdcmHeader::GetElValByName(string TagName) {
1509 string pub = GetPubElValByName(TagName);
1512 return GetShaElValByName(TagName);
1516 * \ingroup gdcmHeader
1517 * \brief Searches within elements parsed with the public dictionary
1518 * and then within the elements parsed with the shadow dictionary
1519 * for the element value representation of a given tag.
1521 * Obtaining the VR (Value Representation) might be needed by caller
1522 * to convert the string typed content to caller's native type
1523 * (think of C++ vs Python). The VR is actually of a higher level
1524 * of semantics than just the native C++ type.
1525 * @param TagName name of the researched element.
1526 * @return Corresponding element value representation when it exists,
1527 * and the string "gdcm::Unfound" otherwise.
1529 string gdcmHeader::GetElValRepByName(string TagName) {
1530 string pub = GetPubElValRepByName(TagName);
1533 return GetShaElValRepByName(TagName);
1537 * \ingroup gdcmHeader
1538 * \brief Accesses an existing gdcmElValue in the PubElValSet of this instance
1539 * through it's (group, element) and modifies it's content with
1541 * @param content new value to substitute with
1542 * @param group group of the ElVal to modify
1543 * @param element element of the ElVal to modify
1545 int gdcmHeader::SetPubElValByNumber(string content, guint16 group,
1548 //TODO : homogeneiser les noms : SetPubElValByNumber qui appelle PubElValSet.SetElValueByNumber
1549 // pourquoi pas SetPubElValueByNumber ??
1552 return ( PubElValSet.SetElValueByNumber (content, group, element) );
1556 * \ingroup gdcmHeader
1557 * \brief Accesses an existing gdcmElValue in the PubElValSet of this instance
1558 * through tag name and modifies it's content with the given value.
1559 * @param content new value to substitute with
1560 * @param TagName name of the tag to be modified
1562 int gdcmHeader::SetPubElValByName(string content, string TagName) {
1563 return ( PubElValSet.SetElValueByName (content, TagName) );
1567 * \ingroup gdcmHeader
1568 * \brief Accesses an existing gdcmElValue in the PubElValSet of this instance
1569 * through it's (group, element) and modifies it's length with
1571 * \warning Use with extreme caution.
1572 * @param length new length to substitute with
1573 * @param group group of the ElVal to modify
1574 * @param element element of the ElVal to modify
1575 * @return 1 on success, 0 otherwise.
1578 int gdcmHeader::SetPubElValLengthByNumber(guint32 length, guint16 group,
1580 return ( PubElValSet.SetElValueLengthByNumber (length, group, element) );
1584 * \ingroup gdcmHeader
1585 * \brief Accesses an existing gdcmElValue in the ShaElValSet of this instance
1586 * through it's (group, element) and modifies it's content with
1588 * @param content new value to substitute with
1589 * @param group group of the ElVal to modify
1590 * @param element element of the ElVal to modify
1591 * @return 1 on success, 0 otherwise.
1593 int gdcmHeader::SetShaElValByNumber(string content,
1594 guint16 group, guint16 element) {
1595 return ( ShaElValSet.SetElValueByNumber (content, group, element) );
1599 * \ingroup gdcmHeader
1600 * \brief Accesses an existing gdcmElValue in the ShaElValSet of this instance
1601 * through tag name and modifies it's content with the given value.
1602 * @param content new value to substitute with
1603 * @param ShadowTagName name of the tag to be modified
1605 int gdcmHeader::SetShaElValByName(string content, string ShadowTagName) {
1606 return ( ShaElValSet.SetElValueByName (content, ShadowTagName) );
1610 * \ingroup gdcmHeader
1611 * \brief Parses the header of the file but WITHOUT loading element values.
1613 void gdcmHeader::ParseHeader(bool exception_on_error) throw(gdcmFormatError) {
1614 gdcmElValue * newElValue = (gdcmElValue *)0;
1618 while ( (newElValue = ReadNextElement()) ) {
1619 SkipElementValue(newElValue);
1620 PubElValSet.Add(newElValue);
1625 * \ingroup gdcmHeader
1626 * \brief This predicate, based on hopefully reasonnable heuristics,
1627 * decides whether or not the current gdcmHeader was properly parsed
1628 * and contains the mandatory information for being considered as
1629 * a well formed and usable image.
1630 * @return true when gdcmHeader is the one of a reasonable Dicom file,
1633 bool gdcmHeader::IsReadable(void) {
1634 if ( GetElValByName("Image Dimensions") != "gdcm::Unfound"
1635 && atoi(GetElValByName("Image Dimensions").c_str()) > 4 ) {
1638 if ( GetElValByName("Bits Allocated") == "gdcm::Unfound" )
1640 if ( GetElValByName("Bits Stored") == "gdcm::Unfound" )
1642 if ( GetElValByName("High Bit") == "gdcm::Unfound" )
1644 if ( GetElValByName("Pixel Representation") == "gdcm::Unfound" )
1650 * \ingroup gdcmHeader
1651 * \brief Small utility function that creates a new manually crafted
1652 * (as opposed as read from the file) gdcmElValue with user
1653 * specified name and adds it to the public tag hash table.
1654 * \note A fake TagKey is generated so the PubDict can keep it's coherence.
1655 * @param NewTagName The name to be given to this new tag.
1656 * @param VR The Value Representation to be given to this new tag.
1657 * @ return The newly hand crafted Element Value.
1659 gdcmElValue* gdcmHeader::NewManualElValToPubDict(string NewTagName, string VR) {
1660 gdcmElValue* NewElVal = (gdcmElValue*)0;
1661 guint32 StuffGroup = 0xffff; // Group to be stuffed with additional info
1662 guint32 FreeElem = 0;
1663 gdcmDictEntry* NewEntry = (gdcmDictEntry*)0;
1665 FreeElem = PubElValSet.GenerateFreeTagKeyInGroup(StuffGroup);
1666 if (FreeElem == UINT32_MAX) {
1667 dbg.Verbose(1, "gdcmHeader::NewManualElValToPubDict",
1668 "Group 0xffff in Public Dict is full");
1669 return (gdcmElValue*)0;
1671 NewEntry = new gdcmDictEntry(StuffGroup, FreeElem,
1672 VR, "GDCM", NewTagName);
1673 NewElVal = new gdcmElValue(NewEntry);
1674 PubElValSet.Add(NewElVal);
1679 * \ingroup gdcmHeader
1680 * \brief Loads the element values of all the elements present in the
1681 * public tag based hash table.
1683 void gdcmHeader::LoadElements(void) {
1685 TagElValueHT ht = PubElValSet.GetTagHt();
1686 for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) {
1687 LoadElementValue(tag->second);
1689 // Load 'non string' values
1691 string PhotometricInterpretation = GetPubElValByNumber(0x0028,0x0004);
1692 if( PhotometricInterpretation == "PALETTE COLOR " ){
1693 LoadElementVoidArea(0x0028,0x1200); // gray LUT
1694 LoadElementVoidArea(0x0028,0x1201); // R LUT
1695 LoadElementVoidArea(0x0028,0x1202); // G LUT
1696 LoadElementVoidArea(0x0028,0x1203); // B LUT
1698 LoadElementVoidArea(0x0028,0x1221); // Segmented Red Palette Color LUT Data
1699 LoadElementVoidArea(0x0028,0x1222); // Segmented Green Palette Color LUT Data
1700 LoadElementVoidArea(0x0028,0x1223); // Segmented Blue Palette Color LUT Data
1705 * \ingroup gdcmHeader
1709 void gdcmHeader::PrintPubElVal(std::ostream & os) {
1710 PubElValSet.Print(os);
1714 * \ingroup gdcmHeader
1718 void gdcmHeader::PrintPubDict(std::ostream & os) {
1719 RefPubDict->Print(os);
1723 * \ingroup gdcmHeader
1727 int gdcmHeader::Write(FILE * fp, FileType type) {
1728 return PubElValSet.Write(fp, type);
1732 // ------------------------ 'non string' elements related functions
1736 * \ingroup gdcmHeader
1737 * \brief Loads (from disk) the element content
1738 * when a string is not suitable
1740 void * gdcmHeader::LoadElementVoidArea(guint16 Group, guint16 Elem) {
1741 gdcmElValue * Element= PubElValSet.GetElementByNumber(Group, Elem);
1744 size_t o =(size_t)Element->GetOffset();
1745 fseek(fp, o, SEEK_SET);
1746 int l=Element->GetLength();
1747 void * a = malloc(l);
1749 cout << "Big Broblem (LoadElementVoidArea, malloc) "
1750 << hex << Group << " " << Elem << "\n";
1753 int res = PubElValSet.SetVoidAreaByNumber(a, Group, Elem);
1754 // TODO check the result
1755 size_t l2 = fread(a, 1, l ,fp);
1757 cout << "Big Broblem (LoadElementVoidArea, fread) "
1758 << hex << Group << " " << Elem << "\n";
1765 * \ingroup gdcmHeader
1766 * \brief Gets (from Header) the offset of a 'non string' element value
1767 * \ (LoadElementValue has already be executed)
1770 * @return File Offset of the Element Value
1772 size_t gdcmHeader::GetPubElValOffsetByNumber(guint16 Group, guint16 Elem) {
1773 gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem);
1775 dbg.Verbose(1, "gdcmHeader::GetElValueByNumber",
1776 "failed to Locate gdcmElValue");
1779 return elValue->GetOffset();
1783 * \ingroup gdcmHeader
1784 * \brief Gets (from Header) a 'non string' element value
1785 * \ (LoadElementValue has already be executed)
1788 * @return Pointer to the 'non string' area
1791 void * gdcmHeader::GetPubElValVoidAreaByNumber(guint16 Group, guint16 Elem) {
1792 gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem);
1794 dbg.Verbose(1, "gdcmHeader::GetElValueByNumber",
1795 "failed to Locate gdcmElValue");
1798 return elValue->GetVoidArea();
1803 // =============================================================================
1804 // Heuristics based accessors
1805 //==============================================================================
1808 // TODO : move to an other file.
1812 * \ingroup gdcmHeader
1813 * \brief Retrieve the number of columns of image.
1814 * @return The encountered size when found, 0 by default.
1816 int gdcmHeader::GetXSize(void) {
1817 // We cannot check for "Columns" because the "Columns" tag is present
1818 // both in IMG (0028,0011) and OLY (6000,0011) sections of the dictionary.
1819 string StrSize = GetPubElValByNumber(0x0028,0x0011);
1820 if (StrSize == "gdcm::Unfound")
1822 return atoi(StrSize.c_str());
1826 * \ingroup gdcmHeader
1827 * \brief Retrieve the number of lines of image.
1828 * \warning The defaulted value is 1 as opposed to gdcmHeader::GetXSize()
1829 * @return The encountered size when found, 1 by default.
1831 int gdcmHeader::GetYSize(void) {
1832 // We cannot check for "Rows" because the "Rows" tag is present
1833 // both in IMG (0028,0010) and OLY (6000,0010) sections of the dictionary.
1834 string StrSize = GetPubElValByNumber(0x0028,0x0010);
1835 if (StrSize != "gdcm::Unfound")
1836 return atoi(StrSize.c_str());
1840 // The Rows (0028,0010) entry is optional for ACR/NEMA. It might
1841 // hence be a signal (1d image). So we default to 1:
1846 * \ingroup gdcmHeader
1847 * \brief Retrieve the number of planes of volume or the number
1848 * of frames of a multiframe.
1849 * \warning When present we consider the "Number of Frames" as the third
1850 * dimension. When absent we consider the third dimension as
1851 * being the "Planes" tag content.
1852 * @return The encountered size when found, 1 by default.
1854 int gdcmHeader::GetZSize(void) {
1855 // Both in DicomV3 and ACR/Nema the consider the "Number of Frames"
1856 // as the third dimension.
1857 string StrSize = GetPubElValByNumber(0x0028,0x0008);
1858 if (StrSize != "gdcm::Unfound")
1859 return atoi(StrSize.c_str());
1861 // We then consider the "Planes" entry as the third dimension [we
1862 // cannot retrieve by name since "Planes tag is present both in
1863 // IMG (0028,0012) and OLY (6000,0012) sections of the dictionary].
1864 StrSize = GetPubElValByNumber(0x0028,0x0012);
1865 if (StrSize != "gdcm::Unfound")
1866 return atoi(StrSize.c_str());
1871 * \ingroup gdcmHeader
1872 * \brief Retrieve the number of Bits Stored
1873 * (as opposite to number of Bits Allocated)
1875 * @return The encountered number of Bits Stored, 0 by default.
1877 int gdcmHeader::GetBitsStored(void) {
1878 string StrSize = GetPubElValByNumber(0x0028,0x0101);
1879 if (StrSize == "gdcm::Unfound")
1881 return atoi(StrSize.c_str());
1886 * \ingroup gdcmHeader
1887 * \brief Retrieve the number of Samples Per Pixel
1888 * (1 : gray level, 3 : RGB)
1890 * @return The encountered number of Samples Per Pixel, 1 by default.
1892 int gdcmHeader::GetSamplesPerPixel(void) {
1893 string StrSize = GetPubElValByNumber(0x0028,0x0002);
1894 if (StrSize == "gdcm::Unfound")
1895 return 1; // Well, it's supposed to be mandatory ...
1896 return atoi(StrSize.c_str());
1900 * \ingroup gdcmHeader
1901 * \brief Retrieve the Planar Configuration for RGB images
1902 * (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
1904 * @return The encountered Planar Configuration, 0 by default.
1906 int gdcmHeader::GetPlanarConfiguration(void) {
1907 string StrSize = GetPubElValByNumber(0x0028,0x0006);
1908 if (StrSize == "gdcm::Unfound")
1910 return atoi(StrSize.c_str());
1914 * \ingroup gdcmHeader
1915 * \brief Return the size (in bytes) of a single pixel of data.
1916 * @return The size in bytes of a single pixel of data.
1919 int gdcmHeader::GetPixelSize(void) {
1920 string PixelType = GetPixelType();
1921 if (PixelType == "8U" || PixelType == "8S")
1923 if (PixelType == "16U" || PixelType == "16S")
1925 if (PixelType == "32U" || PixelType == "32S")
1927 dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
1932 * \ingroup gdcmHeader
1933 * \brief Build the Pixel Type of the image.
1934 * Possible values are:
1935 * - 8U unsigned 8 bit,
1936 * - 8S signed 8 bit,
1937 * - 16U unsigned 16 bit,
1938 * - 16S signed 16 bit,
1939 * - 32U unsigned 32 bit,
1940 * - 32S signed 32 bit,
1941 * \warning 12 bit images appear as 16 bit.
1944 string gdcmHeader::GetPixelType(void) {
1946 BitsAlloc = GetElValByName("Bits Allocated");
1947 if (BitsAlloc == "gdcm::Unfound") {
1948 dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
1949 BitsAlloc = string("16");
1951 if (BitsAlloc == "12")
1952 BitsAlloc = string("16");
1955 Signed = GetElValByName("Pixel Representation");
1956 if (Signed == "gdcm::Unfound") {
1957 dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
1958 BitsAlloc = string("0");
1961 Signed = string("U");
1963 Signed = string("S");
1965 return( BitsAlloc + Signed);
1969 * \ingroup gdcmHeader
1970 * \brief gets the info from 0028,0030 : Pixel Spacing
1972 * @return X dimension of a pixel
1974 float gdcmHeader::GetXSpacing(void) {
1975 float xspacing, yspacing;
1976 string StrSpacing = GetPubElValByNumber(0x0028,0x0030);
1978 if (StrSpacing == "gdcm::Unfound") {
1979 dbg.Verbose(0, "gdcmHeader::GetXSpacing: unfound Pixel Spacing (0028,0030)");
1982 if( sscanf( StrSpacing.c_str(), "%f\\%f", &xspacing, &yspacing) != 2)
1989 * \ingroup gdcmHeader
1990 * \brief gets the info from 0028,0030 : Pixel Spacing
1992 * @return Y dimension of a pixel
1994 float gdcmHeader::GetYSpacing(void) {
1995 float xspacing, yspacing;
1996 string StrSpacing = GetPubElValByNumber(0x0028,0x0030);
1998 if (StrSpacing == "gdcm::Unfound") {
1999 dbg.Verbose(0, "gdcmHeader::GetYSpacing: unfound Pixel Spacing (0028,0030)");
2002 if( sscanf( StrSpacing.c_str(), "%f\\%f", &xspacing, &yspacing) != 2)
2004 if (yspacing == 0.) {
2005 dbg.Verbose(0, "gdcmHeader::GetYSpacing: gdcmData/CT-MONO2-8-abdo.dcm problem");
2006 // seems to be a bug in the header ...
2007 sscanf( StrSpacing.c_str(), "%f\\0\\%f", &xspacing, &yspacing);
2014 *\ingroup gdcmHeader
2015 *\brief gets the info from 0018,0088 : Space Between Slices
2016 *\ else from 0018,0050 : Slice Thickness
2018 * @return Z dimension of a voxel-to be
2020 float gdcmHeader::GetZSpacing(void) {
2021 // TODO : translate into English
2022 // Spacing Between Slices : distance entre le milieu de chaque coupe
2023 // Les coupes peuvent etre :
2024 // jointives (Spacing between Slices = Slice Thickness)
2025 // chevauchantes (Spacing between Slices < Slice Thickness)
2026 // disjointes (Spacing between Slices > Slice Thickness)
2027 // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
2028 // ca interesse le physicien de l'IRM, pas le visualisateur de volumes ...
2029 // Si le Spacing Between Slices est absent,
2030 // on suppose que les coupes sont jointives
2032 string StrSpacingBSlices = GetPubElValByNumber(0x0018,0x0088);
2034 if (StrSpacingBSlices == "gdcm::Unfound") {
2035 dbg.Verbose(0, "gdcmHeader::GetZSpacing: unfound StrSpacingBSlices");
2036 string StrSliceThickness = GetPubElValByNumber(0x0018,0x0050);
2037 if (StrSliceThickness == "gdcm::Unfound")
2040 // if no 'Spacing Between Slices' is found,
2041 // we assume slices join together
2042 // (no overlapping, no interslice gap)
2043 // if they don't, we're fucked up
2044 return atof(StrSliceThickness.c_str());
2046 return atof(StrSpacingBSlices.c_str());
2051 // Image Position Patient (0020,0032):
2052 // If not found (ACR_NEMA) we try Image Position (0020,0030)
2053 // If not found (ACR-NEMA), we consider Slice Location (0020,1041)
2054 // or Location (0020,0050)
2055 // as the Z coordinate,
2056 // 0. for all the coordinates if nothing is found
2058 // TODO : find a way to inform the caller nothing was found
2059 // TODO : How to tell the caller a wrong number of values was found?
2062 * \ingroup gdcmHeader
2063 * \brief gets the info from 0020,0032 : Image Position Patient
2064 *\ else from 0020,0030 : Image Position (RET)
2066 * @return up-left image corner position
2068 float gdcmHeader::GetXImagePosition(void) {
2069 float xImPos, yImPos, zImPos;
2070 string StrImPos = GetPubElValByNumber(0x0020,0x0032);
2072 if (StrImPos == "gdcm::Unfound") {
2073 dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position Patient (0020,0032)");
2074 StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
2075 if (StrImPos == "gdcm::Unfound") {
2076 dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position (RET) (0020,0030)");
2077 // How to tell the caller nothing was found ?
2081 if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
2087 * \ingroup gdcmHeader
2088 * \brief gets the info from 0020,0032 : Image Position Patient
2089 * \ else from 0020,0030 : Image Position (RET)
2091 * @return up-left image corner position
2093 float gdcmHeader::GetYImagePosition(void) {
2094 float xImPos, yImPos, zImPos;
2095 string StrImPos = GetPubElValByNumber(0x0020,0x0032);
2097 if (StrImPos == "gdcm::Unfound") {
2098 dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position Patient (0020,0032)");
2099 StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
2100 if (StrImPos == "gdcm::Unfound") {
2101 dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position (RET) (0020,0030)");
2102 // How to tell the caller nothing was found ?
2106 if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
2112 * \ingroup gdcmHeader
2113 * \brief gets the info from 0020,0032 : Image Position Patient
2114 * \ else from 0020,0030 : Image Position (RET)
2115 * \ else from 0020,1041 : Slice Location
2116 * \ else from 0020,0050 : Location
2118 * @return up-left image corner position
2120 float gdcmHeader::GetZImagePosition(void) {
2121 float xImPos, yImPos, zImPos;
2122 string StrImPos = GetPubElValByNumber(0x0020,0x0032);
2123 if (StrImPos != "gdcm::Unfound") {
2124 if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
2125 dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position Patient (0020,0032)");
2126 return 0.; // bug in the element 0x0020,0x0032
2131 StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
2132 if (StrImPos != "gdcm::Unfound") {
2133 if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
2134 dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position (RET) (0020,0030)");
2135 return 0.; // bug in the element 0x0020,0x0032
2140 string StrSliceLocation = GetPubElValByNumber(0x0020,0x1041);// for *very* old ACR-NEMA images
2141 if (StrSliceLocation != "gdcm::Unfound") {
2142 if( sscanf( StrSliceLocation.c_str(), "%f", &zImPos) !=1) {
2143 dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Slice Location (0020,1041)");
2144 return 0.; // bug in the element 0x0020,0x1041
2149 dbg.Verbose(0, "gdcmHeader::GetZImagePosition: unfound Slice Location (0020,1041)");
2150 string StrLocation = GetPubElValByNumber(0x0020,0x0050);
2151 if (StrLocation != "gdcm::Unfound") {
2152 if( sscanf( StrLocation.c_str(), "%f", &zImPos) !=1) {
2153 dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Location (0020,0050)");
2154 return 0.; // bug in the element 0x0020,0x0050
2159 dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Location (0020,0050)");
2160 return 0.; // Hopeless
2165 * \ingroup gdcmHeader
2166 * \brief gets the info from 0002,0010 : Transfert Syntax
2168 * @return Transfert Syntax Name (as oposite to Transfert Syntax UID)
2170 string gdcmHeader::GetTransferSyntaxName(void) {
2171 string TransfertSyntax = GetPubElValByNumber(0x0002,0x0010);
2172 if (TransfertSyntax == "gdcm::Unfound") {
2173 dbg.Verbose(0, "gdcmHeader::GetTransferSyntaxName: unfound Transfert Syntax (0002,0010)");
2174 return "Uncompressed ACR-NEMA";
2176 // we do it only when we need it
2177 gdcmTS * ts = gdcmGlobal::GetTS();
2178 string tsName=ts->GetValue(TransfertSyntax);
2179 //delete ts; // Seg Fault when deleted ?!
2183 // -------------------------------- Lookup Table related functions ------------
2186 * \ingroup gdcmHeader
2187 * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
2189 * @return Lookup Table Length
2190 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2193 int gdcmHeader::GetLUTLength(void) {
2194 vector<string> tokens;
2198 // Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
2199 string LutDescriptionR = GetPubElValByNumber(0x0028,0x1101);
2200 if (LutDescriptionR == "gdcm::Unfound")
2202 string LutDescriptionG = GetPubElValByNumber(0x0028,0x1102);
2203 if (LutDescriptionG == "gdcm::Unfound")
2205 string LutDescriptionB = GetPubElValByNumber(0x0028,0x1103);
2206 if (LutDescriptionB == "gdcm::Unfound")
2208 if( (LutDescriptionR != LutDescriptionG) || (LutDescriptionR != LutDescriptionB) ) {
2209 dbg.Verbose(0, "gdcmHeader::GetLUTLength: The CLUT R,G,B are not equal");
2212 cout << "Lut Description " << LutDescriptionR <<"\n";
2213 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
2214 Tokenize (LutDescriptionR, tokens, "\\");
2215 LutLength=atoi(tokens[0].c_str());
2216 //LutDepth=atoi(tokens[1].c_str());
2217 //LutNbits=atoi(tokens[2].c_str());
2223 * \ingroup gdcmHeader
2224 * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
2226 * @return Lookup Table nBit
2227 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2230 int gdcmHeader::GetLUTNbits(void) {
2231 vector<string> tokens;
2235 // Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
2236 // Consistency already checked in GetLUTLength
2237 string LutDescription = GetPubElValByNumber(0x0028,0x1101);
2238 if (LutDescription == "gdcm::Unfound")
2240 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
2241 Tokenize (LutDescription, tokens, "\\");
2242 //LutLength=atoi(tokens[0].c_str());
2243 //LutDepth=atoi(tokens[1].c_str());
2244 LutNbits=atoi(tokens[2].c_str());
2251 * \ingroup gdcmHeader
2252 * \brief gets the info from 0028,1201 : Lookup Table Red
2254 * @return Lookup Table Red
2255 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2257 void * gdcmHeader::GetLUTRed(void) {
2258 return GetPubElValVoidAreaByNumber(0x0028,0x1201);
2262 * \ingroup gdcmHeader
2263 * \brief gets the info from 0028,1202 : Lookup Table Green
2265 * @return Lookup Table Red
2266 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2268 void * gdcmHeader::GetLUTGreen(void) {
2269 return GetPubElValVoidAreaByNumber(0x0028,0x1202);
2273 * \ingroup gdcmHeader
2274 * \brief gets the info from 0028,1202 : Lookup Table Blue
2276 * @return Lookup Table Blue
2277 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2279 void * gdcmHeader::GetLUTBlue(void) {
2280 return GetPubElValVoidAreaByNumber(0x0028,0x1203);
2284 * \ingroup gdcmHeader
2286 * @return Lookup Table RGB
2287 * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
2288 * \ and (0028,1201),(0028,1202),(0028,1202) are found
2289 * \warning : hazardous ! Use better GetPubElValVoidAreaByNumber
2291 void * gdcmHeader::GetLUTRGB(void) {
2292 // Not so easy : see
2293 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
2294 // and OT-PAL-8-face.dcm
2296 if (GetPubElValByNumber(0x0028,0x0004) == "gdcm::Unfound") {
2297 dbg.Verbose(0, "gdcmHeader::GetLUTRGB: unfound Photometric Interpretation");
2300 void * LutR,*LutG,*LutB;
2303 // Maybe, some day we get an image
2304 // that respects the definition ...
2305 // Let's consider no ones does.
2310 int nBits=GetLUTNbits();
2311 // a virer quand on aura trouve UNE image
2312 // qui correspond VRAIMENT à la definition !
2313 cout << "l " << l << " nBits " << nBits;
2317 LutR =GetPubElValVoidAreaByNumber(0x0028,0x1201);
2318 LutG =GetPubElValVoidAreaByNumber(0x0028,0x1202);
2319 LutB =GetPubElValVoidAreaByNumber(0x0028,0x1203);
2321 // Warning : Any value for nBits as to be considered as 8
2322 // Any value for Length as to be considered as 256
2325 // Just wait before removing the following code
2328 guint16 * LUTRGB, *rgb;
2329 LUTRGB = rgb = (guint16 *) malloc(3*l*sizeof( guint16));
2330 guint16 * r = (guint16 *)LutR;
2331 guint16 * g = (guint16 *)LutG;
2332 guint16 * b = (guint16 *)LutB;
2333 for(int i=0;i<l;i++) {
2341 */ { // we assume it's always 8 Bits
2342 l=256; // we assume ...
2343 unsigned char * LUTRGB, *rgb;
2344 LUTRGB = rgb = (unsigned char *) malloc(3*l*sizeof( char));
2345 unsigned char * r = (unsigned char *)LutR;
2346 unsigned char * g = (unsigned char *)LutG;
2347 unsigned char * b = (unsigned char *)LutB;
2348 for(int i=0;i<l;i++) {
2349 //cout << "lut16 " << i << " : " << *r << " " << *g << " " << *b << "\n";
2350 printf("lut 8 %d : %d %d %d \n",i,*r,*g,*b);
2355 free(LutR); free(LutB); free(LutG);