]> Creatis software - gdcm.git/commitdiff
* More memmory link related corrections and documentation fixes.
authorfrog <frog>
Wed, 16 Apr 2003 21:37:59 +0000 (21:37 +0000)
committerfrog <frog>
Wed, 16 Apr 2003 21:37:59 +0000 (21:37 +0000)
        Notes on valgrind:
          - maximum info is obtained with a command of the form:
            valgrind --leak-check=yes --leak-resolution=high --num-callers=40
                   --show-reachable=yes PrintHeader
          - the remaining reachable blocks seem to come from the STL
            allocation scheme through the usage of map and list. It looks
            like this memory cannot be freed but it is not a memory leak
            (in fact further invocation to the STL would recollect the
             unused memory allthough it cannot explicitely be freed).
      * gdcmPython/demo/vtkGdcmDemo.py added: this is a small demo
        of displaying an image parsed with gdcm and displayed with VTK.
        Note: some images don't seem to work e.g.
            python vtkGdcmDemo.py  ../../Data/US-RGB-8-esopecho.dcm
      * src/gdcmHeader.x: dicom_vr and Dicts are not class members anymore.
        Allthough this weakens the semantics, it is a ditch attempt to
        make gdcm more thread friendly.   --- Frog

ChangeLog
gdcmPython/demo/vtkGdcmDemo.py [new file with mode: 0644]
gdcmPython/gdcm.i
src/gdcmFile.cxx
src/gdcmHeader.cxx
src/gdcmHeader.h

index c3da4560956b388f0782e7b6e43642b402da4afa..c7ea5813adeb2182f4defcc07a6164b926f0aa4d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2003-04-16  Eric Boix <Eric.Boix@creatis.insa-lyon.fr> with JPR
       * More memmory link related corrections and documentation fixes.
+        Notes on valgrind:
+          - maximum info is obtained with a command of the form:
+            valgrind --leak-check=yes --leak-resolution=high --num-callers=40
+                   --show-reachable=yes PrintHeader
+          - the remaining reachable blocks seem to come from the STL
+            allocation scheme through the usage of map and list. It looks 
+            like this memory cannot be freed but it is not a memory leak
+            (in fact further invocation to the STL would recollect the
+             unused memory allthough it cannot explicitely be freed).
+      * gdcmPython/demo/vtkGdcmDemo.py added: this is a small demo
+        of displaying an image parsed with gdcm and displayed with VTK.
+        Note: some images don't seem to work e.g.
+            python vtkGdcmDemo.py  ../../Data/US-RGB-8-esopecho.dcm
+      * src/gdcmHeader.x: dicom_vr and Dicts are not class members anymore.
+        Allthough this weakens the semantics, it is a ditch attempt to
+        make gdcm more thread friendly.
 
 2003-04-15  Eric Boix <Eric.Boix@creatis.insa-lyon.fr> with JPR
       * Memory link hunt (by using valgrind through the command
diff --git a/gdcmPython/demo/vtkGdcmDemo.py b/gdcmPython/demo/vtkGdcmDemo.py
new file mode 100644 (file)
index 0000000..142066a
--- /dev/null
@@ -0,0 +1,205 @@
+## A small demo that displays with VTK an image parsed with gdcm.
+import sys
+import vtk
+from gdcmPython import gdcmHeader
+
+class vtkHistogram:
+   '''Some medical images might have a poor dynamic. This is because
+      some additional visual tags-like the patient name- are sometimes
+      written/drawn on top of the image : to avoid interference with
+      the image itself, the level used for this additional text is
+      usually way above the maximum level of the image. Hence we use
+      some cumulative histograme die-hard technique to remove some
+      arbitrary small portion of the dynamic.'''
+   def __init__(self, imagereader):
+      self.vtkReader  = imagereader
+      self.__Histo = None
+      self.__CumulHisto = None
+      self.__LevelMin = self.vtkReader.GetOutput().GetScalarRange()[0]
+      self.__LevelMax = self.vtkReader.GetOutput().GetScalarRange()[1]
+      self.__NumberOfBins = 255   # See VTKImageAccumulate
+      self.__Spacing = (self.__LevelMax - self.__LevelMin)/self.__NumberOfBins
+
+   def ComputeHisto(self):
+      self.__Histo = vtk.vtkImageAccumulate()
+      self.__Histo.SetComponentExtent(0, self.__NumberOfBins, 0, 0, 0, 0)
+      self.__Histo.SetInput(self.vtkReader.GetOutput())
+      self.__Histo.SetComponentSpacing(self.__Spacing, 0.0, 0.0)
+      self.__Histo.Update()
+
+   def ComputeCumulativeHisto(self):
+      ''' Compyte the Cumulative histogram of the image.'''
+      if not self.__Histo:
+         self.ComputeHisto()
+      self.__CumulHisto = []
+      histo = self.__Histo.GetOutput()
+      self.__CumulHisto.append(int(histo.GetScalarComponentAsFloat(0,0,0,0)))
+      for i in range(1, self.__NumberOfBins):
+         value = int(histo.GetScalarComponentAsFloat(i,0,0,0))
+         self.__CumulHisto.append( self.__CumulHisto[i-1] + value)
+
+   def GetTruncateLevels(self, LostPercentage):
+      ''' Compute the level below which (respectively above which)
+          the LostPercentage percentage of the pixels shall be disregarded.
+      '''
+      if LostPercentage < 0.0 or LostPercentage >1.0:
+         print "   vtkHistogram::GetTruncateLevels: defaulting to 5%"
+         LostPercentage = 0.05
+      if not self.__CumulHisto:
+         self.ComputeCumulativeHisto()
+      NumPixels     = self.__CumulHisto[self.__NumberOfBins - 1]
+      NumLostPixels = NumPixels * LostPercentage
+      AboveLevel = None
+      BelowLevel = None
+      # FIXME : not really clean loop...
+      for i in range(0, self.__NumberOfBins-1):
+         if not BelowLevel:
+            if self.__CumulHisto[i] > NumLostPixels:
+               BelowLevel = self.__LevelMin + self.__Spacing * i
+         if not AboveLevel:
+            if self.__CumulHisto[i] > NumPixels - NumLostPixels:
+               AboveLevel = self.__LevelMin + self.__Spacing * i
+      if not AboveLevel or not BelowLevel:
+         print "   vtkHistogram::GetTruncateLevels: Mistakes were made..."
+      return BelowLevel, AboveLevel
+
+class vtkGdcmImage(gdcmHeader):
+   ''' All informations required for VTK display
+       * VTK pipeline
+       * Lut (or associated Window/Level reductive control parameters)'''
+   def __init__(self, filename):
+      gdcmHeader.__init__(self, filename) 
+      self.filename = filename
+                               # LUT reductive parameters
+      self.__LevelMin  = 0     # Minimum value of pixel intensity
+      self.__LevelMax  = 0     # Maximun value of pixel intensity
+      self.__Level     = 0     # Current Level value (treshold below
+                               # which the image is represented as black)
+      self.__Window    = 0     # Width of displayed pixels (linearly).
+                               # Above Level + Window pixel are represented
+                               # as white.
+      self.__VTKplaneActor = None
+      self.vtkSetup()
+
+   def VTKreaderSetup(self):
+      self.__VTKReader = vtk.vtkImageReader()
+      self.__VTKReader.SetFileName(self.filename)
+      type = self.GetPixelType()
+      if type == "8U":
+          self.__VTKReader.SetDataScalarTypeToUnsignedChar()
+      elif type == "8S":
+          self.__VTKReader.SetDataScalarTypeTodChar()
+      elif type == "16U":
+          self.__VTKReader.SetDataScalarTypeToUnsignedShort()
+      elif type == "16S":
+          self.__VTKReader.SetDataScalarTypeToShort()
+          self.__VTKReader.SetDataByteOrderToLittleEndian()
+          #self.__VTKReader.SetDataByteOrderToBigEndian()
+      else:
+          print "vtkGdcmImage::VTKreaderSetup: unkown encoding:", type
+          sys.exit()
+      self.__VTKReader.SetHeaderSize(self.GetPixelOffset())
+      self.__VTKReader.SetDataExtent (0, self.GetXSize()-1,
+                                      0, self.GetYSize()-1,
+                                      1, 1)
+      self.__VTKReader.UpdateWholeExtent()
+
+   def vtkSetup(self, orgx = -0.5, orgy = -0.5,
+                      ix = 0.5, iy = -0.5, jx = -0.5, jy = 0.5):
+      # ImageReader ---> Texture        \
+      #                                  | ==> PlaneActor
+      # PlaneSource ---> PolyDataMapper /
+      self.VTKreaderSetup()
+      ### LookupTable
+      self.__VTKtable = vtk.vtkLookupTable()
+      self.__VTKtable.SetNumberOfColors(1000)
+      self.__VTKtable.SetTableRange(0,1000)
+      self.CallibrateWindowLevel()    # calls the SetTableRange
+      self.__VTKtable.SetSaturationRange(0,0)
+      self.__VTKtable.SetHueRange(0,1)
+      self.__VTKtable.SetValueRange(0,1)
+      self.__VTKtable.SetAlphaRange(1,1)
+      self.__VTKtable.Build()
+      ### Texture
+      self.__VTKtexture = vtk.vtkTexture()
+      self.__VTKtexture.SetInput(self.__VTKReader.GetOutput())
+      self.__VTKtexture.InterpolateOn()
+      self.__VTKtexture.SetLookupTable(self.__VTKtable)
+      ### PlaneSource
+      self.__VTKplane = vtk.vtkPlaneSource()
+      self.__VTKplane.SetOrigin( orgx, orgy, 0.0)
+      self.__VTKplane.SetPoint1( ix,   iy,   0.0)
+      self.__VTKplane.SetPoint2( jx,   jy,   0.0)
+      ### PolyDataMapper
+      self.__VTKplaneMapper = vtk.vtkPolyDataMapper()
+      self.__VTKplaneMapper.SetInput(self.__VTKplane.GetOutput())
+      ### Actor
+      self.__VTKplaneActor = vtk.vtkActor()
+      self.__VTKplaneActor.SetTexture(self.__VTKtexture)
+      self.__VTKplaneActor.SetMapper(self.__VTKplaneMapper)
+      self.__VTKplaneActor.PickableOn()
+
+   def CallibrateWindowLevel(self):
+      vtkreader = self.__VTKReader
+      self.__LevelMin  = vtkreader.GetOutput().GetScalarRange()[0]
+      self.__LevelMax  = vtkreader.GetOutput().GetScalarRange()[1]
+      histo = vtkHistogram(vtkreader)
+      self.__Level, above = histo.GetTruncateLevels(0.05)
+      self.__Window = above - self.__Level
+      self.__VTKtable.SetTableRange(self.__Level,
+                                    self.__Level + self.__Window)
+
+   def GetVTKActor(self):
+      return self.__VTKplaneActor
+
+######################################################################
+import os
+from gdcmPython import GDCM_DATA_PATH
+
+### Get filename from command line or default it
+try:
+   FileName = sys.argv[1]
+except IndexError:
+   FileName = os.path.join(GDCM_DATA_PATH, "test.acr")
+
+if not os.path.isfile(FileName):
+   print "Cannot open file ", FileName
+   sys.exit()
+
+### The default vtkImageReader is unable to read some type of images:
+check = gdcmHeader(FileName)
+if not check.IsReadable():
+   print "The ", FileName, " file is not "
+   print "   readable with gdcm. Sorry."
+   sys.exit()
+check = check.GetPubElVal()
+try:
+   BitsAlloc = check["Bits Allocated"]
+   if len(BitsAlloc) == 0 or BitsAlloc == "gdcm::Unfound":
+      raise KeyError
+except KeyError:
+   print "Gdcm couldn't find the Bits Allocated Dicom tag in file ", FileName
+   sys.exit()
+try:
+   BitsStored = check["Bits Stored"]
+   if len(BitsStored) == 0 or BitsStored == "gdcm::Unfound":
+      raise KeyError
+except KeyError:
+   print "Gdcm couldn't find the Bits Stored Dicom tag in file ", FileName
+   sys.exit()
+if BitsAlloc != BitsStored:
+   print "vtkImageReader cannot read the file ", FileName
+   print "  because the Bits Allocated and the Bits stored don't match."
+   sys.exit()
+
+### Display in a vtk RenderWindow
+image = vtkGdcmImage(FileName)
+ren = vtk.vtkRenderer()
+renwin = vtk.vtkRenderWindow()
+renwin.AddRenderer(ren)
+iren = vtk.vtkRenderWindowInteractor()
+iren.SetRenderWindow(renwin)
+ren.AddActor(image.GetVTKActor())
+ren.SetBackground(0,0,0.5)
+renwin.Render()
+iren.Start()
index f89537bbae71e750abe96fb3fa818496eb8e5825..41d5ee2080708e256e2da6137d9f51081a64ed03 100644 (file)
@@ -20,6 +20,7 @@ void EatLeadingAndTrailingSpaces(string & s) {
 typedef  unsigned short guint16;
 typedef  unsigned int guint32;
 
+////////////////////////////////////////////////////////////////////////////
 %typemap(out) list<string> * {
        PyObject* NewItem = (PyObject*)0;
        PyObject* NewList = PyList_New(0); // The result of this typemap
@@ -31,6 +32,7 @@ typedef  unsigned int guint32;
        $result = NewList;
 }
 
+////////////////////////////////////////////////////////////////////////////
 // Convert a c++ hash table in a python native dictionary
 %typemap(out) map<string, list<string> > * {
        PyObject* NewDict = PyDict_New(); // The result of this typemap
@@ -55,6 +57,7 @@ typedef  unsigned int guint32;
        $result = NewDict;
 }
 
+////////////////////////////////////////////////////////////////////////////
 // Convert a c++ hash table in a python native dictionary
 %typemap(out) TagElValueHT & {
        PyObject* NewDict = PyDict_New(); // The result of this typemap
@@ -84,6 +87,13 @@ typedef  unsigned int guint32;
        $result = NewDict;
 }
 
+////////////////////////////////////////////////////////////////////////////
+// Deals with function returning a C++ string.
+%typemap(out) string  {
+    $result = PyString_FromString(($1).c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////
 %include "gdcmCommon.h"
 %include "gdcmDictEntry.h"
 %include "gdcmDict.h"
index c103e111bfe3711ef8e087351dd9e4e4507d0e1d..5e39b7018122ea781b749beca50a172308c57d4f 100644 (file)
@@ -280,7 +280,7 @@ return;
  * \brief TODO JPR
  * \warning doit-etre etre publique ?  FIXME JPR
  *
- * @param Data TODO JPR
+ * @param inData TODO JPR
  * @param ExpectedSize TODO JPR
  *
  * @return TODO JPR    
index 82c31a0fa29d00f8664537d6cf6ff56c9df250d0..10404615a6d4098fb49f11ad4ef68abc9488656d 100644 (file)
 // Refer to gdcmHeader::SetMaxSizeLoadElementValue()
 #define _MaxSizeLoadElementValue_   1024
 
-gdcmVR * gdcmHeader::dicom_vr = (gdcmVR*)0;
-gdcmDictSet * gdcmHeader::Dicts    = (gdcmDictSet*)0;
-
 void gdcmHeader::Initialise(void) {
-   if (!gdcmHeader::dicom_vr)
-      gdcmHeader::dicom_vr = gdcmGlobal::GetVR();
-   if (!gdcmHeader::Dicts)
-      gdcmHeader::Dicts = gdcmGlobal::GetDicts();
+   dicom_vr = gdcmGlobal::GetVR();
+   Dicts = gdcmGlobal::GetDicts();
    RefPubDict = Dicts->GetDefaultPubDict();
    RefShaDict = (gdcmDict*)0;
 }
index 2741b3edee997de85c81a28ade6387f5d873c271..b55056717de81199305c6a5075bc1174029b9793 100644 (file)
@@ -37,10 +37,9 @@ class GDCM_EXPORT gdcmHeader {
 private:
    /// Pointer to the Value Representation Hash Table which contains all
    /// the VR of the DICOM version3 public dictionary. 
-   static gdcmVR *dicom_vr;
-   /// Global dictionary container
-   static gdcmDictSet* Dicts;
+   gdcmVR *dicom_vr;     // Not a class member for thread-safety reasons
+   /// Pointer to global dictionary container
+   gdcmDictSet* Dicts;   // Not a class member for thread-safety reasons
    /// Public dictionary used to parse this header
    gdcmDict* RefPubDict;
    /// Optional "shadow dictionary" (private elements) used to parse this