Class SoVolumeReader

  • All Implemented Interfaces:
    SafeDisposable
    Direct Known Subclasses:
    SoLDMReader, SoVRAmFileReader, SoVRAvsFileReader, SoVRDicomFileReader, SoVRGenericFileReader, SoVRImageDataReader, SoVRMemoryReader, SoVRMrcFileReader, SoVRRasterStackReader, SoVRSegyFileReader, SoVRTiffFileReader, SoVRVolFileReader, SoVRVoxFileReader, SoVRXtFileReader

    public abstract class SoVolumeReader
    extends SoFieldContainer
    Abstract base class for volume data set readers. This virtual class provides the interface through which VolumeViz accesses volume data that is not already in memory. Predefined reader classes are provided for many common formats like DICOM and SEGY. See the list of supported file formats and class names below.

    Application developers may implement custom reader classes. Custom reader classes allow VolumeViz to access data stored in other file formats. This is particularly useful for converting data to LDM format. A custom reader class could also allow VolumeViz to access data through a proprietary data manager or data stored in application memory.

    Minimum implementation:

    The application must implement a class derived from SoVolumeReader, and must implement the methods getDataChar() and getSubSlice(). In addition, the optional methods getNumSignificantBits() and getMinMax() can be implemented. If getNumSignificantBits() is not implemented (or returns 0), then the number of significant bits depends on the data type. If getMinMax() is not implemented (or returns false), then VolumeViz will find the min and max voxel values. Note that this requires loading all the data and could take a lot of time for a large volume.

    VolumeViz always calls getDataChar() and getNumSignificantBits() before requesting any data from the reader.

    If the isDataConverted() method is not implemented (or returns false), then VolumeViz will only call getSubSlice() to request data and will build the LDM tile hierarchy in memory, using the slice data. In this case, note that:

    • VolumeViz always caches data in memory using an LDM multi-resolution, tiled hierarchy. Therefore getSubSlice() may be called multiple times with the same slice number as tiles are constructed in memory.
    • The tile dimensions used to cache data in memory are determined by the tileDimension field of the SoLDMResourceParameters object associated with the SoVolumeData node. This field can be set by the application, but VolumeViz will not automatically set it (as it does in the case of an LDM reader). Of course a custom reader could implement the getTileSize() method for the application to call, even though VolumeViz doesn't use it.
    • VolumeViz will not call the getHistogram() method. So if the application calls getHistogram() on the SoVolumeData node, VolumeViz will build the histogram. This requires loading all the data and could take a lot of time for a large volume. However if VolumeViz builds the histogram, it also finds the min and max values, so there is no extra time required for that query.

    LDM (tiled) volume reader:

    In addition to the usual requirements for an application-defined reader, e.g., implementing the getDataChar() method, an application-defined reader for LDM data is required to:

    • Implement the isDataConverted() method and return true.
      The method must return true when the reader is passed to SoVolumeData to ensure that LDM manager classes are correctly initialized.
    • Implement the readTile() method.
      • When implementing the readTile method, be aware that how the SoCpuBuffer object is created affects how its memory will be managed. In general you should create the object using the constructor with no parameters, then allocate memory inside the object using the setSize() method. In this case the buffer object owns the memory and the memory will be freed when LDM no longer needs this tile. This implies for example that to avoid data copying when reading tile data from a file, you should create and allocate the buffer object, map the buffer object, then read the data into the buffer object. If you create the buffer object "wrapping" an existing block of memory, that memory will not be freed when LDM releases the buffer object. The application owns that memory and is responsible for freeing it.
      • Note that readTile can return one of the specialized sub-classes of SoCpuBufferObject that use less system memory:
        • SoCpuBufferUniform: This buffer contains a "uniform" tile in which all the voxels have the same value. It is stored efficiently as a single value until needed.
        • SoCpuBufferCompressed: This buffer contains a compressed tile. It is stored efficiently in its compressed form until needed.
        • SoCpuBufferBasicProperty: This buffer contains a standard tile, but can also store the tile's min and max data values for use in various optimizations.
        • SoCpuBufferBitset: This contains a bitset tile in which each voxel is a boolean value. It is stored efficiently in a packed form until needed.
      • Also note that readTile can return an undefined tile by returning either NULL or an empty buffer. See readTile() for detail.
    • Implement the getTileSize() method

    An application-defined LDM reader may optionally:

    • Implement the getMinMax() method
      Return true and the requested values if the min and max data values for the volume are known to the reader, e.g., are in the file header. The reader should implement this method if possible, because applications typically query these values to setup their SoDataRange node. If this method is not implemented, and the application calls SoVolumeData.getMinMax() is called, then VolumeViz must load every tile to compute the volume min and max. This can cause a long delay.
    • Implement the getTileMinMax() method
      Return the min and max values for the specified data tile. This information benefits optimizations such as SoLDMGlobalResourceParameters.setIgnoreFullyTransparentTiles. If this method is not implemented, VolumeViz will automatically compute the min and max values for each tile when it is loaded. Automatic computation works fine for volume data, but we strongly recommend implementing this method for height field data.
    • Implement the getHistogram() method
      Return true and the requested values if the min and max data values are known to the reader, e.g., are in the file header.

    General information:

    Starting with Open Inventor 7.0, general rectilinear grids are supported. This feature allows non-uniform voxel spacing along each axis of the volume. The AmiraMesh reader (.am file), the in-memory reader, and the LDM reader support rectilinear coordinates. Call the method setRectilinearCoordinates to specify rectilinear coordinates (if they are not already stored in the data file and set by the reader).

    Starting with Open Inventor 7.0, SoVolumeReader is derived from SoFieldContainer. This allows client classes like SoVolumeData to be automatically notified when the reader's state changes, and update their own state appropriately. Any reader method that changes the volume properties (dimension, size, data type, etc) should trigger notification by calling the reader's touch() method. If this notification is not done, client node fields, for example SoDataSet.extent, won't be updated correctly. For example, a reader with a method setData(SbVec3i32 size, void* data) that loads a new data set should call touch() at its end. This reader could also be implemented using an SoSFArray3D field instead of the setData method. Modifying this field will automatically trigger notification.

    Applications should subclass from SoVolumeReader when creating any type of custom volume reader, including an LDM (tiled) volume reader. The classes SoLDMReader and SoVRLdmFileReader are internal classes which are specific to the VSG defined LDM file format.

    This class cannot be instantiated directly.

    Supported file formats:

    File format notes:

    • Avizo mesh
      Avizo mesh is a general purpose file format that can contain many different kinds of data. The VolumeViz file reader can load Avizo mesh files containing a 3-dimensional "Lattice" data object with uniform coordinates and any data type. See SoVRAmFileReader for limitations.
    • AVS field
      AVS field is a general purpose file format that can contain many different kinds of data. The VolumeViz file reader can load AVS field files containing 3-dimensional, uniform data of type "byte". See SoVRAvsFileReader.
    • DICOM
      A widely used format for storing medical image data (CT, MRI, etc), defined by the National Electrical Manufacturers Association (NEMA) (medical.nema.org). See SoVRDicomFileReader
    • LDM
      LDM is a format defined by VSG for storing hierarchical multi-resolution volume data. VolumeViz includes a utility program that can convert any other format supported by VolumeViz into this format (see SoVolumeConverter). Preprocessing volume data into this format provides the maximum benefits from the VolumeViz large data management (LDM) features. See SoVRLdmFileReader.
    • MRC
      MRC is a file format that has become an industry standard for cryo-electron microscopy (cryoEM) and electron tomography (ET) (http://www.ccpem.ac.uk/mrc_format/mrc2014.php). See SoVRMrcFileReader.
    • SEGY
      A widely used format for storing seismic trace data, defined by the Society of Exploration Geophysicists publication "Digital Tape Standards" (www.seg.org). The VolumeViz reader supports all sizes of integer and float data, and can correctly determine the number of samples per trace in many cases. However the reader also has many options to adapt to differences in SEGY file headers. See SoVRSegyFileReader.
    • VOL
      A simple volume interchange format (see "Introduction to Volume Rendering", Lichtenbelt, Crane, Naqvi, 1998). The VolumeViz reader can load files containing 8- or 16-bit voxels. See SoVRVolFileReader.
    • VOX
      A volume interchange format defined by TeraRecon Inc. (www.terarecon.com). The VolumeViz reader can load "Vox1999a" files containing 8- or 16-bit voxels (first volume only). See SOVRVoxFileReader.
    • LST (stack of images)
      A simple format for loading a stack of images (one image per file). Specify the names of the image files in a .lst file. VolumeViz can load image data in most common image formats including BMP, DDS, GIF, JPEG, JPEG2000, PNG and TIFF. See SoVRRasterStackReader for details and limitations.

    Note: '3D TIFF' files (multiple images in one file) are not currently supported.

    See Also:
    SoDataSet, SoVolumeData, SoConverter
    • Method Detail

      • isDataConverted

        public boolean isDataConverted()
        Returns true if the data is already organized in tiles for the LDM module.
        In other words, all drivers that directly support the getTile() method should return true from isDataConverted. If true is returned, VolumeViz will use the readTile method and will NOT call getSubVolume() or getSubSlice().
      • setFilename

        public int setFilename​(java.lang.String filename)
        Specifies the path of the file. Returns 0 for success. Any other return value indicates failure.
      • getFilename

        public java.lang.String getFilename()
        Returns the path of the file.
      • reloadTileMinMax

        public void reloadTileMinMax()
      • getTileSize

        public SbVec3i32 getTileSize()
        Returns tile dimensions in voxels when using data stored in tiles.
        Return false if data is not stored in tiles.
      • readTile

        public SoBufferObject readTile​(int index,
                                       SbBox3i32 tilePosition)
        Given an index, reads a tile if the data is organized in tiles (for LDM).
        In the default LDM architecture, the LDM data is based on an octree topology (see SoLDMFileReader). The index passed is 0 for the tile of lowest resolution representing the entire volume (octree root node). The index increments linearly going down through the octree.

        A tile can be undefined or empty which means it is not rendered and it isn't taken into account for the rendering of its adjacent cells. An empty buffer or NULL can be returned by this method to specify an undefined tile.

        Parameters:
        index - specifies a fileID, the id of an existing tile (fileID=tileID in a cubical volume).

        tilePosition - specifies the position of the data in the associated volume data of the tile corresponding to the given index. In the default SoVRLdmFileReader, the tilePosition isn't actually used but it is passed as a convenience for customized readers (can be used for mapping to a different index scheme).

        Returns:
        the buffer containing the data of the tile, or NULL (or an empty buffer) in order to specify an undefined tile.

      • setOutputDataType

        public boolean setOutputDataType​(boolean doChange,
                                         SoDataSet.DataTypes outputType)
        Sets the output data type. Returns false if the reader does not support this feature. If the reader does support this feature and doChange is set to true, data is converted before being returned (by getSubSlice() for instance). If doChange is set to false, data is not converted and is returned as is.
      • setInputDataRange

        public boolean setInputDataRange​(boolean doChange,
                                         double min,
                                         double max)
        Requests that the input be converted from the specified range to the range depending on the output data type. This allows, for instance, if the output data type is unsigned byte, conversion of float data from range [min,max] to range [0,255]. If doChange is false no range conversion is applied. Always returns true.
      • getDataChar

        public SoVolumeReader.DataInfo getDataChar()
        Gets the characteristics (file header) of the data volume. See SoDataSet.setVolumeData().
        You can use the convenience method setFilename() to specify the file location, in which case you will not have to open the file yourself. Then you can use the convenience method getBuffer() to read the header in order to get the requested information.

        NOTE: We strongly recommend that readers implement this version of getDataChar, introduced in VolumeViz 5.1, because it uses SbVec3i32 for the volume dimension.

      • getTileMinMax

        public SbVec2d getTileMinMax​(int index)
        Returns the minimum and maximum data values for the given tile. This information benefits optimizations such as SoLDMGlobalResourceParameters.setIgnoreFullyTransparentTiles and custom SoVolumeReader able to return SoCpuBufferUniform.

        VolumeViz will only call this method if the data is organized in tiles like the LDM file format. In other words, if isDataConverted() returned true. The LDM Converter program automatically computes this information and stores it in the LDM header file. Custom volume readers that implement tiled data, i.e. return true when isDataConverted is called, should consider implement this method when the min max values are available from their backend API.

        NOTES:

        • Automatic computation of tile min/max values works fine for actual volume data. But we strongly recommend implementing this method for height field data (see SoHeightFieldGeometry etc). Because of the way the height field algorithm works, if tile min/max info is not available, VolumeViz must load all height field tiles before the first render. This can cause a long delay before the first rendering appears.
        • When all the voxels in the requested tile (fileId) have the same value, we recommend that the volume reader implement two optimizations. First, the readTile() method should return an SoCpuBufferUniform object (instead of SoCpuBufferObject). This reduces the amount of CPU and GPU memory needed to store the tile, allowing more tiles to be loaded in the same amount of memory. Second, the getTileMinMax() method should return an empty range (min = max) for this tile. This allows VolumeViz to optimize sending tiles to the GPU. However, note that the getTileMinMax() method is called before the readTile() method. So ideally the volume reader should be able to return the tile min/max without actually loading the tile data, for example by using metadata stored with the tiles.

        Default implementation returns new SbVec2d(Double.MAX_VALUE, -Double.MAX_VALUE)

        Parameters:
        index - The fileId of the tile.
      • getAppropriateReader

        public static SoVolumeReader getAppropriateReader​(java.lang.String filename)
        Returns a preconfigured SoVolumeReader instance that can be used to load the given file (based on the filename extension). For example, would return an instance of SoVRDicomFileReader for a file named "data.dic".

        If no SoVolumeReader is associated to this extension through registerVolumeReaderExtension() then NULL is returned.

        Since:
        Open Inventor 9.4

      • getNumVoxels

        public SbVec3i32 getNumVoxels​(SbVec3i32 realSize,
                                      SbVec3i32 subsamplingLevel)
        Utility function provided by SoVolumeReader for subclass readers to call. Returns the size of the brick the reader must use, based on subsamplingLevel and realSize of the brick. If the subsampling level is greater than 0 on an axis, the corresponding size is computed as follows:
        1. realSize is divided by 2** subsamplingLevel
        2. brickSize is the next greater power of 2

        For example, if subsamplingLevel[0]=1 and realSize[0]=21, then the returned readerSize[0]=16.

        If subsamplingLevel is 0, the corresponding size is the realSize.

      • getSizeToAllocate

        public SbVec3i32 getSizeToAllocate​(SbVec3i32 realSize,
                                           SbVec3i32 subsamplingLevel)
        Utility function provided by SoVolumeReader for subclass readers to call. If the reader uses the NO_COPY policy, this method returns the size to allocate for the brick. For each axis of the brick, this size is:

        (first power of 2 greater than realSize) / ( 2** subsamplingLevel)

      • getSubSlice

        public void getSubSlice​(SbBox2i32 subSlice,
                                int sliceNumber,
                                SoBufferObject dataBuffer)
        This method is called by Open Inventor during an LDM conversion or during volume loading. It must be overridden to define a custom reader. The implementation must copy the rectangular part defined by subSlice of the XY slice sliceNumber to the given buffer object.

        NOTES

        When loading or converting a large volume data, the size of the given bufferObject may exceed 2^31 elements. In that case, it is not possible to get the Java array that backs the bufferObject because an array cannot contain more than 2^31 elements in Java. That may lead to generate a Java.lang.IllegalArgumentException with a negative capacity message.

        That occurs in the following situation: In order to build the root tile of the model, LDM call getSubSlice with a buffer of size S = Si * Sj * data type size. Si is the lowest power of 2 bigger than the I dimension of the volume. Sj is the lowest power of 2 bigger than the J dimension of the volume. For instance if the volume contains 17000*35000*1500 short values, a buffer of size 32768*65536*2 bytes is requested. This example will lead to an exception.

        Parameters:
        subSlice - 2D region of the slice to return.

        sliceNumber - Slice number on the volume Z axis (first slice is 0).

        dataBuffer - The target buffer to be filled.
      • getNumSignificantBits

        public int getNumSignificantBits()
        This method is optional. It returns the number of significant bits of the volume data.

        If it is not implemented, the default return value is 0, meaning the number of bits depends on the data type. See the last parameter of SoVolumeData.setVolumeData(). This method is called immediately after getDataChar(). For float data type, number of bits is forced to 32.

      • readTile

        @Deprecated
        public boolean readTile​(int index,
                                SoBufferObject buffer,
                                SbBox3i32 tilePosition)
        Deprecated.
        As of Open Inventor 8500. Use SoBufferObject* readTile(int index, const SbBox3i32& tilePosition) instead.
        Same as readTile(int index, unsigned char*& buffer, const SbBox3i32& tilePosition) but using an SoBufferObject (allocated by LDM) as the target of the data. If not reimplemented then the version with "unsigned char*" parameter is called

        Warning Deprecated since Open Inventor 8500. Use SoBufferObject* readTile(int index, const SbBox3i32& tilePosition) instead.

      • getBorderFlag

        @Deprecated
        public int getBorderFlag()
        Deprecated.
        As of Open Inventor 9000. No longer used. Only kept to read old file format that contains borderFlag.
        Returns border (overlap) value if stored in file.

        Warning Deprecated since Open Inventor 9000. No longer used. Only kept to read old file format that contains borderFlag.

      • getIntMinMax

        @Deprecated
        public int[] getIntMinMax()
        Deprecated.
        As of Open Inventor 9500. Use getMinMax(int64_t & min, int64_t & max) instead.
        Returns min max if stored in file.

        Warning Deprecated since Open Inventor 9500. Use getMinMax(int64_t & min, int64_t & max) instead.

      • setRectilinearCoordinates

        public void setRectilinearCoordinates​(float[] x,
                                              float[] y,
                                              float[] z)
        Sets rectilinear coordinates for the data set.
      • setRGBA

        public void setRGBA​(boolean flag)
        Specifies if data must be considered as RGBA.
      • closeAllHandles

        public void closeAllHandles()
        Close all resources that are locked by the reader. Allows other classes to use the same resources. For example, if the reader reads from a file, this method closes the file so that an SoVolumeWriter associated with the same file can re-open it in write mode. The reader remembers the resources that were closed, so they can re-opened by calling restoreAllHandles().
      • restoreAllHandles

        public void restoreAllHandles()
        Restore resources that were closed by closeAllHandles().
      • isRGBA

        public boolean isRGBA()
        Returns true if the data set contains RGBA color values.
      • getMinMax

        public long[] getMinMax()
        Returns min and max for integer data type, if available. If not available, return false. The reader should implement this method if possible, because applications often query these values to setup their SoDataRange node. If this method is not implemented, and the application calls SoVolumeData.getMinMax() is called, then VolumeViz must load every tile to compute the volume min and max. This can cause a long delay.
      • getDoubleMinMax

        public double[] getDoubleMinMax()
        Returns min max for float data type, if available. If not available, return false. The reader should implement this method if possible, because applications often query these values to setup their SoDataRange node. If this method is not implemented, and the application calls SoVolumeData.getMinMax() is called, then VolumeViz must load every tile to compute the volume min and max. This can cause a long delay.
      • getOriginalFilename

        public java.lang.String getOriginalFilename()
        Returns original file name from which the data has been converted to LDM format if stored in file.
      • isThreadSafe

        public boolean isThreadSafe()
        Should return true if the reader is thread safe.
        This function is called by VolumeViz when using the multiIO mode (LDM only). LDM multi-threaded data loading can only be used if this method returns true.
      • isIgnoredFile

        public boolean isIgnoredFile()
        Should return true if at least one file has been ignored during reading.
      • setDirectCoordSysAutoDetection

        public boolean setDirectCoordSysAutoDetection​(boolean autoValue)
        Sets whether or not the reader should automatically try to detect if the coordinate system used is direct or not. The function will return false if the feature is not supported by the current reader. Default is false.
      • getConfiguredWriter

        public SoVolumeWriter getConfiguredWriter()
        Returns a volume writer that corresponds to this reader
        (same format, parameters, etc). If no writer can be created, NULL is returned.

        Notes:

        • The simplest way to implement this behavior is to return a new writer every time.
        • The returned writer is not synchronized with this reader nor with other writers.

        For example:

         MyVolumeReader reader = new MyVolumeReader();
         reader.setFilename( "someFile.dat"  );
         MyVolumeWriter writer1 = (MyVolumeWriter)reader.getConfiguredWriter();
         // This writer will write to "someFile.dat"
         
         reader.setFilename( "someOtherFile.dat" );
         // The writer will still write to "someFile.dat"
         
         writer1.setFilename( "stillAnotherFile.dat" );
         SoVolumeWriter writer2 = reader.getConfiguredWriter();
         // writer2 will write to "someOtherFile.dat", because
         // the reader is configured to read from "someOtherFile.dat"
      • getDirectCoordSys

        public boolean getDirectCoordSys()
        Return whether the coordinate system used is direct or not.
      • getDirectCoordSysAutoDetection

        public boolean getDirectCoordSysAutoDetection()
        Return automatic detection value.
      • setDirectCoorSys

        public boolean setDirectCoorSys​(boolean directCoord)
        Specify if the coordinate system used is direct or not. The function will return false if the feature is not supported by the current reader. Default is true.