From 4a0c2407ec81e041c2e43781acec4c78c83fc487 Mon Sep 17 00:00:00 2001 From: frog Date: Fri, 11 Apr 2003 15:50:51 +0000 Subject: [PATCH] Clean up with JPR. Frog --- ChangeLog | 14 +++ Doc/DoxyfileDeveloppers | 2 +- gdcmPython/testSuite.py | 60 +++++------ src/Makefile.am | 12 +-- src/gdcmDict.cxx | 2 +- src/gdcmDict.h | 2 +- src/gdcmElValSet.cxx | 3 - src/gdcmFile.cxx | 218 +++++++++++++++++++++------------------- src/gdcmFile.h | 56 +++-------- src/gdcmHeader.cxx | 110 ++++++++++++-------- src/gdcmHeader.h | 26 ++--- 11 files changed, 261 insertions(+), 244 deletions(-) diff --git a/ChangeLog b/ChangeLog index a7f69cca..66c84436 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2003-04-9 Eric Boix with JPR + * src/Makefile.am now exports all the necessary include files at + make install stage. + 2003-04-9 JPR * UpdateGroupLength replaced by new one @@ -7,11 +11,21 @@ 2003-04-7 Eric Boix with JPR * Data/im_00001 renamed to gdcm-MR-PHILIPS-16-Multi-Seq.dcm + * gdcmPython/testSuite.py now include a test of gdcmFile::Write. * src: - gdcmHeader::GetPubElValSet removed. - gdcmElValSet::WriteDcm, WriteAcr, WriteExplVR, revamped to UpdateGroupLength, WriteElements, Write. + - gdcmHeader::FileType moved to gdcmCommon.h. The enum FileType + doesn't contain TrueDicom anymore since ExplicitVR and ImplicitVR + carried the same semantics. + - src/gdcmHeaderIdo.cxx changed accordingly. + - gdcmFile::WriteBase now regroups all the codes from previous + versions of WriteDcm, WriteDcmImplVR, and WriteACR. - enum FileType moved to gdcmCommon.h + * src/gdcmHeader.cxx AddDefault trashed + * gdcmGetXSize added + * getimageDataSize now calls gdcmGetXSize * Test/*.cxx changed to agree with above changes 2003-03-31 Eric Boix diff --git a/Doc/DoxyfileDeveloppers b/Doc/DoxyfileDeveloppers index 74b7efa8..7f960e04 100644 --- a/Doc/DoxyfileDeveloppers +++ b/Doc/DoxyfileDeveloppers @@ -323,7 +323,7 @@ RECURSIVE = NO # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = +EXCLUDE = ../src/iddcmjpeg.h ../src/gdcmJpeg.cxx # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. diff --git a/gdcmPython/testSuite.py b/gdcmPython/testSuite.py index 4537915a..14934598 100644 --- a/gdcmPython/testSuite.py +++ b/gdcmPython/testSuite.py @@ -21,7 +21,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "FUJI PHOTO FILM CO. LTD."], ["Manufacturer Model Name", "9000"], - ["7fe0|0010", "gdcm::NotLoaded. Address:776 Length:387200"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:776 Length:387200"] ] ], #################################### # CT modality examples: #################################### @@ -36,7 +36,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "1"], ["Manufacturer", "Picker International, Inc."], ["Manufacturer Model Name", "PQ5000"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1680 Length:524288"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1680 Length:524288"] ] ], ["CT-MONO2-16-ort.dcm", [ ["Transfer Syntax UID", "1.2.840.10008.1.2"], # Implicit VR, LE ["Modality", "CT"], @@ -48,7 +48,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "1"], ["Manufacturer", "GE MEDICAL SYSTEMS"], ["Manufacturer Model Name", "HiSpeed CT/i"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1674 Length:524288"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1674 Length:524288"] ] ], ["CT-MONO2-16-ankle.dcm", [ ["Transfer Syntax UID", "1.2.840.10008.1.2"], # Implicit, little ["Modality", "CT"], @@ -60,7 +60,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "1"], ["Manufacturer", "GE MEDICAL SYSTEMS"], ["Manufacturer Model Name", "GENESIS_ZEUS"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1148 Length:524288"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1148 Length:524288"] ] ], ["CT-MONO2-8-abdo.dcm", [ ["Transfer Syntax UID", "1.2.840.10008.1.2"], # Implicit, little ["Modality", "CT"], @@ -71,7 +71,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Manufacturer", "FUJI"], - ["7fe0|0010", "gdcm::NotLoaded. Address:796 Length:262144"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:796 Length:262144"] ] ], ["CT-MONO2-12-lomb-an2.acr2", [ ["Modality", "CT"], #"Transfer Syntax UID" and "Photometric Interpretation" are absent. @@ -82,7 +82,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "11"], ["Pixel Representation", "0"], ["Manufacturer", "SIEMENS"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1230 Length:524288"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1230 Length:524288"] ] ], ["CT-MONO2-16-chest.dcm", [ ["Transfer Syntax UID", "1.2.840.10008.1.2.4.70"], # jpeg... ["Modality", "CT"], @@ -94,7 +94,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "15"], ["Pixel Representation", "1"], ["Manufacturer", "GE MEDICAL SYSTEMS"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1638 Length:143498"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1638 Length:143498"] ] ], #################################### ### MR modality examples: #################################### @@ -110,7 +110,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "Philips"], # Note: 256*256*12/8 = 98304 - ["7fe0|0010", "gdcm::NotLoaded. Address:650 Length:98304"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:650 Length:98304"] ] ], ["MR-MONO2-12-an2.acr2", [ ["Modality", "MR"], ["Photometric Interpretation", "MONOCHROME2"], @@ -122,7 +122,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "SIEMENS"], ["Manufacturer Model Name", "MAGNETOM VISION"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1866 Length:131072"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1866 Length:131072"] ] ], ["MR-MONO2-16-head.dcm", [ ["Transfer Syntax UID", "1.2.840.10008.1.2"], # Imp VR, LE ["Modality", "MR"], @@ -135,7 +135,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "1"], ["Manufacturer", "GE MEDICAL SYSTEMS"], ["Manufacturer Model Name", "GENESIS_SIGNA"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1804 Length:131072"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1804 Length:131072"] ] ], # MR-MONO2-12-shoulder example is present in the Jpeg section. # MR-MONO2-16-knee is not present in the test suite since it is too # closely related to MR-MONO2-16-head.dcm to be of interest. @@ -155,7 +155,7 @@ class gdcmTestCase(unittest.TestCase): ["Bits Allocated", "8"], ["High Bit", "7"], ["Pixel Representation", "0"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1654 Length:307200"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1654 Length:307200"] ] ], # OT-MONO2-8-colon is not present in the test suite since it is too # closely related to OT-MONO2-8-a7 to be of interest. ["OT-MONO2-8-a7.dcm", @@ -169,7 +169,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Manufacturer", "Philips Medical Systems"], - ["7fe0|0010", "gdcm::NotLoaded. Address:438 Length:262144"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:438 Length:262144"] ] ], #################################### # US (Ultra Sound) modality examples: #################################### @@ -187,7 +187,7 @@ class gdcmTestCase(unittest.TestCase): ["Manufacturer", "Acme Products"], # FIXME: 92160 / (120*256) = 3 bytes per pixel NOT 1. Maybe # it has something to do with [Samples Per Pixel] = [3] ??? - ["7fe0|0010", "gdcm::NotLoaded. Address:904 Length:92160"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:904 Length:92160"] ] ], ["US-RGB-8-epicard.dcm", # Interest: Big endian transfert syntax [ ["Transfer Syntax UID", "1.2.840.10008.1.2.2"], # Big endian @@ -204,7 +204,7 @@ class gdcmTestCase(unittest.TestCase): # FIXME: 921600/(480*640) = 3 bytes per pixel NOT 1. Maybe # it has something to do with [Samples Per Pixel] = [3] ??? ["Implementation Version Name", "OFFIS-DCMTK-311"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1012 Length:921600"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1012 Length:921600"] ] ], ] MultiFrameFiles = [ @@ -223,7 +223,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "Philips"], # Note: 256*256*16=1048576 (16 is the number of Frames) - ["7fe0|0010", "gdcm::NotLoaded. Address:920 Length:1048576"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:920 Length:1048576"] ] ], ["NM-MONO2-16-13x-heart.dcm", # Interest: NM modality, multi-frame [ ["Transfer Syntax UID", "1.2.840.10008.1.2.1"], # ExpVR, LE @@ -238,7 +238,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "ACME Products"], # Note: 64*64*13*2=106496 - ["7fe0|0010", "gdcm::NotLoaded. Address:1234 Length:106496"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1234 Length:106496"] ] ], ["US-MONO2-8-8x-execho.dcm", # Interest: multi-frame [ ["Transfer Syntax UID", "1.2.840.10008.1.2.1"], # ExpVR, LE @@ -252,7 +252,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Manufacturer", "Acme Products"], - ["7fe0|0010", "gdcm::NotLoaded. Address:976 Length:122880"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:976 Length:122880"] ] ], ["US-PAL-8-10x-echo.dcm", # Interest: RLE (Run Length Encoded) compression, multiframe [ ["Transfer Syntax UID", "1.2.840.10008.1.2.5"], # RLE @@ -266,7 +266,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Manufacturer", "ACME Products"], - ["7fe0|0010", "gdcm::NotLoaded. Address:2428 Length:481182"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:2428 Length:481182"] ] ], ["XA-MONO2-8-12x-catheter.dcm", # Interest: XA modality, multi-frame [ ["Transfer Syntax UID", "1.2.840.10008.1.2.4.70"], # jpeg @@ -280,7 +280,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Manufacturer", "Acme Products"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1006 Length:920072"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1006 Length:920072"] ] ], ] GdcmFiles = [ @@ -298,7 +298,7 @@ class gdcmTestCase(unittest.TestCase): ["Manufacturer Model Name", "GBS III"], # FIXME: this image looks padded at the end. The length of the file # is 140288. Notice that, 256*256*2 + 1024 = 131072 + 1024 = 132096 - ["7fe0|0010", "gdcm::NotLoaded. Address:8192 Length:132096"], + ["Pixel Data", "gdcm::NotLoaded. Address:8192 Length:132096"], # Oddities: "Study ID" and "Series Number" are empty ["Study ID", ""], ["Series Number", ""] ] ], @@ -321,7 +321,7 @@ class gdcmTestCase(unittest.TestCase): ["Series Instance UID", "1.3.12.2.1107.5.2.4.7630.20000918175714000007"], ["Series Number", "7"], - ["7fe0|0010", "gdcm::NotLoaded. Address:6052 Length:524288"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:6052 Length:524288"] ] ], ["gdcm-US-ALOKA-16.dcm", # Interest: - possesses 3 LUTS: a Green (checked), a Red and BLue. # - announced as implicit VR, but really explicit VR. @@ -338,7 +338,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "ALOKA CO., LTD."], ["Manufacturer Model Name", "SSD-4000"], - ["7fe0|0010", "gdcm::NotLoaded. Address:258740 Length:614400"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:258740 Length:614400"] ] ], ["gdcm-MR-PHILIPS-16.dcm", # Interest: - possesses a sequence # - dicom file, with a recognition code of ACR-NEMA1 @@ -355,7 +355,7 @@ class gdcmTestCase(unittest.TestCase): ["Manufacturer", "Philips Medical Systems"], ["Manufacturer Model Name", "Gyroscan Intera"], ["Sequence Variant", "OTHER"], - ["7fe0|0010", "gdcm::NotLoaded. Address:6584 Length:131072"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:6584 Length:131072"] ] ], ["gdcm-MR-PHILIPS-16-Multi-Seq.dcm", # Interest: - possesses many sequences in group 0x0029 # - Big sequence 28808 bytes at (0x0029, 0x263d) @@ -373,7 +373,7 @@ class gdcmTestCase(unittest.TestCase): ["Manufacturer", "Philips Medical Systems"], ["Manufacturer Model Name", "Gyroscan Intera"], ["Sequence Variant", "OTHER"], - ["7fe0|0010", "gdcm::NotLoaded. Address:35846 Length:32768"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:35846 Length:32768"] ] ], ["gdcm-MR-PHILIPS-16-NonRectPix.dcm", # Interest: - pixels are non rectangular # - private elements are in explicit VR (which is normal) @@ -389,7 +389,7 @@ class gdcmTestCase(unittest.TestCase): ["Manufacturer", "Philips Medical Systems"], ["Manufacturer Model Name", "Gyroscan Intera"], ["Pixel Spacing", "0.487416\\0.194966"], - ["7fe0|0010", "gdcm::NotLoaded. Address:5010 Length:20480"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:5010 Length:20480"] ] ], ["gdcm-CR-DCMTK-16-NonSamplePerPix.dcm", # Interest: - Misses the "Samples Per Pixel" element which prevents # e-film from reading it. @@ -404,7 +404,7 @@ class gdcmTestCase(unittest.TestCase): ["High Bit", "7"], ["Pixel Representation", "0"], ["Implementation Version Name", "OFFIS_DCMTK_341"], - ["7fe0|0010", "gdcm::NotLoaded. Address:740 Length:562500"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:740 Length:562500"] ] ], ] GdcmJpegFiles = [ @@ -421,7 +421,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "SIEMENS"], ["Manufacturer Model Name", "Volume Zoom"], - ["7fe0|0010", "gdcm::NotLoaded. Address:2946 Length:192218"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:2946 Length:192218"] ] ], ["MR-MONO2-12-shoulder.dcm", # Interest: Jpeg compression [Lossless, non-hierar. (14)] [ ["Transfer Syntax UID", "1.2.840.10008.1.2.4.57"], @@ -435,7 +435,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "Philips Medical Systems"], ["Manufacturer Model Name", "Gyroscan NT"], - ["7fe0|0010", "gdcm::NotLoaded. Address:1580 Length:718948"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:1580 Length:718948"] ] ], ["gdcm-JPEG-LossLess3a.dcm", # Interest: - Jpeg compression [Lossless, hierar., first-order # pred. 14, Select. Val. 1] @@ -490,7 +490,7 @@ class gdcmTestCase(unittest.TestCase): ["Pixel Representation", "0"], ["Manufacturer", "Philips Medical Systems"], ["Manufacturer Model Name", "Cassette Holder Type 9840 500 35201"], - ["7fe0|0010", "gdcm::NotLoaded. Address:3144 Length:4795668"] ] ], + ["Pixel Data", "gdcm::NotLoaded. Address:3144 Length:4795668"] ] ], ] def _BaseTest(self, FileSet): @@ -525,7 +525,7 @@ class gdcmTestCase(unittest.TestCase): Source.GetImageData() TargetFileName = "junk" Target = Source.WriteDcmImplVR(TargetFileName) - Sign = 'c7d6bedae1bef3851f35b29952fbbd4b' + Sign = '5af8739c15dd579dea223eb3930cacda' ComputeSign = md5.new(open(TargetFileName).read()).hexdigest() #print ComputeSign self.assertEqual(ComputeSign, Sign, diff --git a/src/Makefile.am b/src/Makefile.am index 590b3e45..17438bae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,16 +34,16 @@ libgdcmincludedir = $(includedir) libgdcminclude_HEADERS = \ gdcmException.h \ gdcmHeader.h \ - gdcmFile.h - -EXTRA_DIST = \ - gdcmUtil.h \ - gdcm.h \ - gdcmIdo.h \ gdcmCommon.h \ + gdcm.h \ gdcmDictSet.h \ gdcmDict.h \ gdcmDictEntry.h \ gdcmElValue.h \ gdcmElValSet.h \ + gdcmFile.h + +EXTRA_DIST = \ + gdcmUtil.h \ + gdcmIdo.h \ iddcmjpeg.h diff --git a/src/gdcmDict.cxx b/src/gdcmDict.cxx index aae1daff..724225bd 100644 --- a/src/gdcmDict.cxx +++ b/src/gdcmDict.cxx @@ -77,7 +77,7 @@ void gdcmDict::PrintByName(ostream& os) { * @param element element of the entry to be found * @return the corresponding dictionnary entry when existing, NULL otherwise */ -gdcmDictEntry * gdcmDict::GetTagByKey(guint16 group, guint16 element) { +gdcmDictEntry * gdcmDict::GetTagByNumber(guint16 group, guint16 element) { TagKey key = gdcmDictEntry::TranslateToKey(group, element); if ( ! KeyHt.count(key)) return (gdcmDictEntry*)0; diff --git a/src/gdcmDict.h b/src/gdcmDict.h index 7fd1b186..04975882 100644 --- a/src/gdcmDict.h +++ b/src/gdcmDict.h @@ -25,7 +25,7 @@ public: int ReplaceEntry(gdcmDictEntry* NewEntry); int RemoveEntry (TagKey key); int RemoveEntry (guint16 group, guint16 element); - gdcmDictEntry * GetTagByKey(guint16 group, guint16 element); + gdcmDictEntry * GetTagByNumber(guint16 group, guint16 element); gdcmDictEntry * GetTagByName(TagName name); void Print(ostream&); void PrintByKey(ostream&); diff --git a/src/gdcmElValSet.cxx b/src/gdcmElValSet.cxx index b6dfbfb9..ed07d4f1 100644 --- a/src/gdcmElValSet.cxx +++ b/src/gdcmElValSet.cxx @@ -195,9 +195,6 @@ void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) { str_trash=trash; elemZ->SetValue(str_trash); } - - // Liberer groupHt ! - } void gdcmElValSet::WriteElements(FileType type, FILE * _fp) { diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index ae8c02b0..c103e111 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -1,8 +1,8 @@ // gdcmFile.cxx #include "gdcmFile.h" - -static void _Swap(void* im, int swap, int lgr, int nb); +#include "gdcmUtil.h" +#include "iddcmjpeg.h" ///////////////////////////////////////////////////////////////// /** @@ -25,18 +25,21 @@ static void _Swap(void* im, int swap, int lgr, int nb); gdcmFile::gdcmFile(string & filename) :gdcmHeader(filename.c_str()) { + SetPixelDataSizeFromHeader(); } gdcmFile::gdcmFile(const char * filename) :gdcmHeader(filename) { + SetPixelDataSizeFromHeader(); } ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile - * \brief Renvoie la longueur A ALLOUER pour recevoir les pixels de l'image + * \brief calcule la longueur (in bytes) A ALLOUER pour recevoir les + * pixels de l'image * ou DES images dans le cas d'un multiframe * ATTENTION : il ne s'agit PAS de la longueur du groupe des Pixels * (dans le cas d'images compressees, elle n'a pas de sens). @@ -44,108 +47,112 @@ gdcmFile::gdcmFile(const char * filename) * @return longueur a allouer */ -size_t gdcmFile::GetImageDataSize(void) { - int nb; - string str_nb; +void gdcmFile::SetPixelDataSizeFromHeader(void) { + int nb; + string str_nb; - str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100); - if (str_nb == "gdcm::Unfound" ) { - nb = 16; - } else { - nb = atoi(str_nb.c_str() ); + str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100); + if (str_nb == "gdcm::Unfound" ) { + nb = 16; + } else { + nb = atoi(str_nb.c_str() ); if (nb == 12) nb =16; - } + } + lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8); +} - size_t lgrTotale = GetXSize() * GetYSize() * GetZSize() *(nb/8); - return (lgrTotale); +/** + * \ingroup gdcmFile + * \brief Accessor + */ +size_t gdcmFile::GetImageDataSize(void) { + return (lgrTotale); } +/** + * \ingroup gdcmFile + * \brief Read pixel data from disk (optionaly decompressing) into the + * caller specified memory location. + * @param destination Where the pixel data should be stored. + * + */ +bool gdcmFile::ReadPixelData(void* destination) { + if ( !OpenFile()) + return false; + if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) { + CloseFile(); + return false; + } + if (IsJPEGLossless()) { + destination = _IdDcmJpegRead(fp); + } else { + size_t ItemRead = fread(destination, lgrTotale, 1, fp); + if ( ItemRead != 1 ) { + CloseFile(); + return false; + } + } + CloseFile(); + return true; +} ///////////////////////////////////////////////////////////////// /** - * \ingroup gdcmFile - * \brief TODO - * \warning WARNING - * + * \ingroup gdcmFile + * \brief Allocates necessary memory, copies the pixel data + * (image[s]/volume[s]) to newly allocated zone and return a + * pointer to it: */ void * gdcmFile::GetImageData (void) { - char * _Pixels; - // Longueur en Octets des Pixels a lire - size_t taille = GetImageDataSize();// ne faudrait-il pas la stocker? - _Pixels = (char *) malloc(taille); - GetImageDataIntoVector(_Pixels, taille); - - // On l'affecte à un champ du dcmFile - Pixels = _Pixels; - lgrTotale = taille; - - // ca fait double emploi, il faudra nettoyer ça - - return(_Pixels); + PixelData = (void *) malloc(lgrTotale); + GetImageDataIntoVector(PixelData, lgrTotale); + return(PixelData); } - - ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile * \brief amene en mémoire dans une zone précisee par l'utilisateur - * les Pixels d'une image NON COMPRESSEE - * \Warning Aucun test n'est fait pour le moment sur le caractere compresse ou non de l'image + * les Pixels d'une image * * @param destination * @param MaxSize * - * @return TODO JPR + * @return The number of bytes actually copied. */ -int gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) { - -// Question : -// dans quel cas la MaxSize sert-elle a quelque chose? -// que fait-on si la taille de l'image est + gde que Maxize? -// que fait-on si la taille de l'image est + petite que Maxize? +size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) { + int nb, nbu, highBit, signe; + string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe; + + if ( lgrTotale > MaxSize ) { + dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger" + "than caller's expected MaxSize"); + return (size_t)0; + } - void * Pixels = destination; // pour garder le code identique avec GetImageData - - int nb, nbu, highBit, signe; - string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe; - - unsigned short int mask = 0xffff; - - // Longueur en Octets des Pixels a lire - size_t _lgrTotale = GetImageDataSize(); // ne faudrait-il pas la stocker? - - // si lgrTotale < MaxSize ==> Gros pb - // -> on résoud à la goret - - if ( _lgrTotale < MaxSize ) MaxSize = _lgrTotale; - - GetPixels(MaxSize, destination); + (void)ReadPixelData(destination); - // Nombre de Bits Alloues pour le stockage d'un Pixel - str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100); - + // Nombre de Bits Alloues pour le stockage d'un Pixel + str_nb = GetPubElValByNumber(0x0028,0x0100); if (str_nb == "gdcm::Unfound" ) { nb = 16; } else { nb = atoi(str_nb.c_str() ); } - // Nombre de Bits Utilises + // Nombre de Bits Utilises str_nbu=GetPubElValByNumber(0x0028,0x0101); - if (str_nbu == "gdcm::Unfound" ) { nbu = nb; } else { nbu = atoi(str_nbu.c_str() ); } - // Position du Bit de Poids Fort + // Position du Bit de Poids Fort str_highBit=GetPubElValByNumber(0x0028,0x0102); - if (str_highBit == "gdcm::Unfound" ) { highBit = nb - 1; } else { @@ -154,39 +161,41 @@ int gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) { // Signe des Pixels str_signe=GetPubElValByNumber(0x0028,0x0103); - if (str_signe == "gdcm::Unfound" ) { signe = 1; } else { signe = atoi(str_signe.c_str() ); } - // On remet les Octets dans le bon ordre si besoin est - if (nb != 8) { - int _sw = GetSwapCode(); - - _Swap (destination, _sw, _lgrTotale, nb); - } - - // On remet les Bits des Octets dans le bon ordre si besoin est - // - // ATTENTION : Jamais confronté a des pixels stockes sur 32 bits - // avec moins de 32 bits utilises - // et dont le bit de poids fort ne serait pas la ou on l'attend ... - // --> ne marchera pas dans ce cas - if (nbu!=nb){ - mask = mask >> (nb-nbu); - int l=(int)MaxSize/(nb/8); - unsigned short *deb = (unsigned short *)Pixels; - for(int i=0;i> (nbu-highBit-1)) & mask; - deb ++; - } - } - - // VOIR s'il ne faudrait pas l'affecter à un champ du dcmHeader - - return 1; + // On remet les Octets dans le bon ordre si besoin est + if (nb != 8) + SwapZone(destination, GetSwapCode(), lgrTotale, nb); + + // On remet les Bits des Octets dans le bon ordre si besoin est + if (nbu != nb){ + int l = (int)lgrTotale / (nb/8); + if (nb == 16) { + guint16 mask = 0xffff; + mask = mask >> (nb-nbu); + guint16 *deb = (guint16 *)destination; + for(int i = 0; i> (nbu-highBit-1)) & mask; + deb ++; + } + } else if (nb == 32 ) { + guint32 mask = 0xffffffff; + mask = mask >> (nb-nbu); + guint32 *deb = (guint32 *)destination; + for(int i = 0; i> (nbu-highBit-1)) & mask; + deb ++; + } + } else { + dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: wierd image"); + return (size_t)0; + } + } + return lgrTotale; } @@ -194,7 +203,7 @@ int gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) { // Je laisse le code integral, au cas ça puisse etre reutilise ailleurs // -static void _Swap(void* im, int swap, int lgr, int nb) { +void gdcmFile::SwapZone(void* im, int swap, int lgr, int nb) { guint32 s32; guint16 fort,faible; int i; @@ -276,14 +285,11 @@ return; * * @return TODO JPR */ -int gdcmFile::SetImageData(void * Data, size_t ExpectedSize) { - - SetImageDataSize(ExpectedSize); - - Pixels = Data; - lgrTotale = ExpectedSize; - - return(1); +int gdcmFile::SetImageData(void * inData, size_t ExpectedSize) { + SetImageDataSize(ExpectedSize); + PixelData = inData; + lgrTotale = ExpectedSize; + return(1); } @@ -339,7 +345,7 @@ int gdcmFile::WriteRawData (string nomFichier) { return (0); } - fwrite (Pixels,lgrTotale, 1, fp1); + fwrite (PixelData,lgrTotale, 1, fp1); fclose (fp1); return(1); } @@ -383,12 +389,12 @@ int gdcmFile::WriteDcmExplVR (string nomFichier) { ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile - * \brief Ecrit sur disque UNE image ACR-NEMA + * \brief Ecrit au format ACR-NEMA sur disque l'entete et les pixels * (a l'attention des logiciels cliniques * qui ne prennent en entrée QUE des images ACR ... - * si un header DICOM est fourni en entree, + * \warning si un header DICOM est fourni en entree, * les groupes < 0x0008 et les groupes impairs sont ignores) - * Aucun test n'est fait sur l'"Endiannerie" du processeur. + * \warning Aucun test n'est fait sur l'"Endiannerie" du processeur. * Ca fonctionnera correctement (?) sur processeur Intel * (Equivalent a IdDcmWrite) * @@ -419,7 +425,7 @@ int gdcmFile::WriteBase (string nomFichier, FileType type) { } gdcmHeader::Write(fp1, type); - fwrite(Pixels, lgrTotale, 1, fp1); + fwrite(PixelData, lgrTotale, 1, fp1); fclose (fp1); return(1); } diff --git a/src/gdcmFile.h b/src/gdcmFile.h index d9a5f3d1..b8158760 100644 --- a/src/gdcmFile.h +++ b/src/gdcmFile.h @@ -10,41 +10,19 @@ // In addition to Dicom header exploration, this class is designed // for accessing the image/volume content. One can also use it to // write Dicom files. -////// QUESTION: this looks still like an open question whether the -////// relationship between a gdcmFile and gdcmHeader is of -////// type IS_A or HAS_A ! class GDCM_EXPORT gdcmFile: public gdcmHeader { private: - // QUESTION : - // Data pointe sur quoi? - // sur les Pixels lus? - // --> j'ajoute un champ public : Pixels - // (il faudra que l'utilisateur puisse modifier les pixels ?) - - void* Data; + void* PixelData; + size_t lgrTotale; int Parsed; // weather allready parsed string OrigFileName; // To avoid file overwrite + void SwapZone(void* im, int swap, int lgr, int nb); + bool ReadPixelData(void * destination); protected: int WriteBase(string FileName, FileType type); public: - // je ne suis pas sur d'avoir compris *où* il serait légitime de ranger ca. - // on pourra tjs le deplacer, et mettre des accesseurs - void * Pixels; - size_t lgrTotale; - - // Constructor dedicated to writing a new DICOMV3 part10 compliant - // file (see SetFileName, SetDcmTag and Write) - // TODO Swig gdcmFile(); - // Opens (in read only and when possible) an existing file and checks - // for DICOM compliance. Returns NULL on failure. - // Note: the in-memory representation of all available tags found in - // the DICOM header is post-poned to first header information access. - // This avoid a double parsing of public part of the header when - // one sets an a posteriori shadow dictionary (efficiency can be - // seen as a side effect). - gdcmFile(string & filename); gdcmFile(const char * filename); @@ -57,29 +35,27 @@ public: // from the constructor's one (no overwriting allowed). // TODO Swig int SetFileName(string filename); - // Allocates necessary memory, copies the data (image[s]/volume[s]) to - // newly allocated zone and return a pointer to it: - - void * GetImageData(); - + void SetPixelDataSizeFromHeader(void); // Returns size (in bytes) of required memory to contain data // represented in this file. - size_t GetImageDataSize(); + // Allocates necessary memory, copies the data (image[s]/volume[s]) to + // newly allocated zone and return a pointer to it: + void * GetImageData(); + // Copies (at most MaxSize bytes) of data to caller's memory space. // Returns an error code on failure (if MaxSize is not big enough) - - int GetImageDataIntoVector(void* destination, size_t MaxSize ); + size_t GetImageDataIntoVector(void* destination, size_t MaxSize ); // Allocates ExpectedSize bytes of memory at this->Data and copies the - // pointed data to it. - - // Question : - // Pourquoi dupliquer les pixels, alors qu'on les a deja en mémoire, - // et que Data (dans le gdcmHeader) est un pointeur ? - + // pointed data to it. Copying the image might look useless but + // the caller might destroy it's image (without knowing it: think + // of a complicated interface where display is done with a library + // e.g. VTK) before calling the Write int SetImageData (void * Data, size_t ExpectedSize); + // When the caller is aware we simply point to the data: + // TODO int SetImageDataNoCopy (void * Data, size_t ExpectedSize); void SetImageDataSize (size_t ExpectedSize); // Push to disk. diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index 4f2d2aff..7588e008 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -13,8 +13,6 @@ #include "gdcmUtil.h" #include "gdcmHeader.h" -#include "iddcmjpeg.h" - // Refer to gdcmHeader::CheckSwap() #define HEADER_LENGTH_TO_READ 256 // Refer to gdcmHeader::SetMaxSizeLoadElementValue() @@ -30,26 +28,40 @@ void gdcmHeader::Initialise(void) { RefShaDict = (gdcmDict*)0; } -gdcmHeader::gdcmHeader(const char *InFilename, bool exception_on_error) - throw(gdcmFileError) { +gdcmHeader::gdcmHeader(const char *InFilename, bool exception_on_error) { SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_); filename = InFilename; Initialise(); - fp=fopen(InFilename,"rb"); + OpenFile(exception_on_error); + ParseHeader(); + LoadElements(); + CloseFile(); +} + +bool gdcmHeader::OpenFile(bool exception_on_error) + throw(gdcmFileError) { + fp=fopen(filename.c_str(),"rb"); if(exception_on_error) { if(!fp) throw gdcmFileError("gdcmHeader::gdcmHeader(const char *, bool)"); } else - dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename); - ParseHeader(); - LoadElements(); + dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", filename.c_str()); + if ( fp ) + return true; + return false; } +bool gdcmHeader::CloseFile(void) { + int closed = fclose(fp); + fp = (FILE *)0; + if (! closed) + return false; + return true; +} gdcmHeader::~gdcmHeader (void) { //FIXME obviously there is much to be done here ! - fclose(fp); return; } @@ -88,6 +100,38 @@ void gdcmHeader::InitVRDict (void) { dicom_vr = vr; } +// Fourth semantics: +// CMD Command +// META Meta Information +// DIR Directory +// ID +// PAT Patient +// ACQ Acquisition +// REL Related +// IMG Image +// SDY Study +// VIS Visit +// WAV Waveform +// PRC +// DEV Device +// NMI Nuclear Medicine +// MED +// BFS Basic Film Session +// BFB Basic Film Box +// BIB Basic Image Box +// BAB +// IOB +// PJ +// PRINTER +// RT Radio Therapy +// DVH +// SSET +// RES Results +// CRV Curve +// OLY Overlays +// PXL Pixels +// + /** * \ingroup gdcmHeader * \brief Discover what the swap code is (among little endian, big endian, @@ -241,19 +285,6 @@ void gdcmHeader::SwitchSwapToBigEndian(void) { sw = 3412; } -void gdcmHeader::GetPixels(size_t lgrTotale, void* _Pixels) { - size_t pixelsOffset; - pixelsOffset = GetPixelOffset(); - fseek(fp, pixelsOffset, SEEK_SET); - if (IsJPEGLossless()) { - _Pixels=_IdDcmJpegRead(fp); - } else { - fread(_Pixels, 1, lgrTotale, fp); - } -} - - - /** * \ingroup gdcmHeader * \brief Find the value representation of the current tag. @@ -617,7 +648,8 @@ void gdcmHeader::FindLength(gdcmElValue * ElVal) { // and the dictionary entry depending on them. guint16 CorrectGroup = SwapShort(ElVal->GetGroup()); guint16 CorrectElem = SwapShort(ElVal->GetElement()); - gdcmDictEntry * NewTag = GetDictEntryByKey(CorrectGroup, CorrectElem); + gdcmDictEntry * NewTag = GetDictEntryByNumber(CorrectGroup, + CorrectElem); if (!NewTag) { // This correct tag is not in the dictionary. Create a new one. NewTag = new gdcmDictEntry(CorrectGroup, CorrectElem); @@ -803,7 +835,7 @@ void gdcmHeader::LoadElementValue(gdcmElValue * ElVal) { return; } - // FIXME The exact size should be length if we move to strings or whatever + // We need an additional byte for storing \0 that is not on disk char* NewValue = (char*)malloc(length+1); if( !NewValue) { dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue"); @@ -864,16 +896,6 @@ guint32 gdcmHeader::ReadInt32(void) { return g; } - - - -// -// TODO : JPR Pour des raisons d'homogeneité de noms, -// remplacer les quelques GetxxxByKey -// par des GetxxxByNumber, lorsqu'on passe gr, el en param. -// il peut etre interessant de rajouter des GetxxxByKey, auxquels on passe *vraiment* une TagKey -// - /** * \ingroup gdcmHeader * \brief Build a new Element Value from all the low level arguments. @@ -882,15 +904,15 @@ guint32 gdcmHeader::ReadInt32(void) { * @param Group group of the underlying DictEntry * @param Elem element of the underlying DictEntry */ -gdcmElValue* gdcmHeader::NewElValueByKey(guint16 Group, guint16 Elem) { +gdcmElValue* gdcmHeader::NewElValueByNumber(guint16 Group, guint16 Elem) { // Find out if the tag we encountered is in the dictionaries: - gdcmDictEntry * NewTag = GetDictEntryByKey(Group, Elem); + gdcmDictEntry * NewTag = GetDictEntryByNumber(Group, Elem); if (!NewTag) NewTag = new gdcmDictEntry(Group, Elem); gdcmElValue* NewElVal = new gdcmElValue(NewTag); if (!NewElVal) { - dbg.Verbose(1, "gdcmHeader::NewElValueByKey", + dbg.Verbose(1, "gdcmHeader::NewElValueByNumber", "failed to allocate gdcmElValue"); return (gdcmElValue*)0; } @@ -904,7 +926,7 @@ gdcmElValue* gdcmHeader::NewElValueByKey(guint16 Group, guint16 Elem) { */ int gdcmHeader::ReplaceOrCreateByNumber(string Value, guint16 Group, guint16 Elem ) { - gdcmElValue* nvElValue=NewElValueByKey(Group, Elem); + gdcmElValue* nvElValue=NewElValueByNumber(Group, Elem); PubElValSet.Add(nvElValue); PubElValSet.SetElValueByNumber(Value, Group, Elem); return(1); @@ -950,7 +972,7 @@ gdcmElValue * gdcmHeader::ReadNextElement(void) { // has to be considered as finished. return (gdcmElValue *)0; - NewElVal = NewElValueByKey(g, n); + NewElVal = NewElValueByNumber(g, n); FindVR(NewElVal); FindLength(NewElVal); if (errno == 1) @@ -1018,7 +1040,8 @@ size_t gdcmHeader::GetPixelOffset(void) { numPixel = 0x1010; else numPixel = 0x0010; - gdcmElValue* PixelElement = PubElValSet.GetElementByNumber(grPixel, numPixel); + gdcmElValue* PixelElement = PubElValSet.GetElementByNumber(grPixel, + numPixel); if (PixelElement) return PixelElement->GetOffset(); else @@ -1035,19 +1058,20 @@ size_t gdcmHeader::GetPixelOffset(void) { * @param element element of the searched DictEntry * @return Corresponding DictEntry when it exists, NULL otherwise. */ -gdcmDictEntry * gdcmHeader::GetDictEntryByKey(guint16 group, guint16 element) { +gdcmDictEntry * gdcmHeader::GetDictEntryByNumber(guint16 group, + guint16 element) { gdcmDictEntry * found = (gdcmDictEntry*)0; if (!RefPubDict && !RefShaDict) { dbg.Verbose(0, "gdcmHeader::GetDictEntry", "we SHOULD have a default dictionary"); } if (RefPubDict) { - found = RefPubDict->GetTagByKey(group, element); + found = RefPubDict->GetTagByNumber(group, element); if (found) return found; } if (RefShaDict) { - found = RefShaDict->GetTagByKey(group, element); + found = RefShaDict->GetTagByNumber(group, element); if (found) return found; } diff --git a/src/gdcmHeader.h b/src/gdcmHeader.h index 5e43f564..b082bd83 100644 --- a/src/gdcmHeader.h +++ b/src/gdcmHeader.h @@ -52,7 +52,6 @@ private: gdcmElValSet ShaElValSet; /// Refering underlying filename. string filename; - FILE * fp; // FIXME sw should be an enum e.g. //enum EndianType { @@ -83,12 +82,12 @@ private: gdcmElValue* NewManualElValToPubDict(string NewTagName, string VR); void SetMaxSizeLoadElementValue(long); - gdcmDictEntry * GetDictEntryByKey(guint16, guint16); + gdcmDictEntry * GetDictEntryByNumber(guint16, guint16); gdcmDictEntry * GetDictEntryByName(string name); // ElValue related utilities gdcmElValue * ReadNextElement(void); - gdcmElValue * NewElValueByKey(guint16 group, guint16 element); + gdcmElValue * NewElValueByNumber(guint16 group, guint16 element); gdcmElValue * NewElValueByName(string name); void FindLength(gdcmElValue *); void FindVR(gdcmElValue *); @@ -99,6 +98,16 @@ private: bool IsAnInteger(gdcmElValue *); void LoadElements(void); +protected: + FILE * fp; + FileType filetype; + bool OpenFile(bool exception_on_error = false) + throw(gdcmFileError); + bool CloseFile(void); + int write(ostream&); + int anonymize(ostream&); // FIXME : anonymize should be a friend ? +public: + bool IsReadable(void); bool IsImplicitVRLittleEndianTransferSyntax(void); bool IsExplicitVRLittleEndianTransferSyntax(void); bool IsDeflatedExplicitVRLittleEndianTransferSyntax(void); @@ -107,24 +116,15 @@ private: bool IsJPEGExtendedProcess2_4TransferSyntax(void); bool IsJPEGExtendedProcess3_5TransferSyntax(void); bool IsJPEGSpectralSelectionProcess6_8TransferSyntax(void); - bool IsJPEGLossless(void); bool IsDicomV3(void); -protected: - FileType filetype; - int write(ostream&); - int anonymize(ostream&); // FIXME : anonymize should be a friend ? -public: - bool IsReadable(void); virtual void ParseHeader(bool exception_on_error = false) throw(gdcmFormatError); - gdcmHeader(const char *filename, bool exception_on_error = false) - throw(gdcmFileError); + gdcmHeader(const char *filename, bool exception_on_error = false); virtual ~gdcmHeader(); size_t GetPixelOffset(void); - void GetPixels(size_t, void *); int GetSwapCode(void) { return sw; } // TODO Swig int SetPubDict(string filename); -- 2.45.1