271 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
The IlmImfUtil Library
 | 
						|
----------------------
 | 
						|
 | 
						|
The IlmImfUtil library implements an in-memory image data structure, as
 | 
						|
well as simple function calls for saving images in OpenEXR files, and for
 | 
						|
constructing images from the contents of existing OpenEXR files.
 | 
						|
 | 
						|
The OpenEXR file format has a fairly large number of options for on-file
 | 
						|
image storage, including arbitrary sets of channels, per-channel pixel
 | 
						|
format selection, sub-sampled channels, multi-resolution images, deep
 | 
						|
images, or storing images as tiles or scan lines.  While reading a simple
 | 
						|
RGBA image does not require a lot of code, reading the contents of an
 | 
						|
arbitrary OpenEXR file, and representing those contents in main memory
 | 
						|
is not trivial.  The IlmImfUtil library simplifies those tasks.
 | 
						|
 | 
						|
Image, Image Level, Image Channel
 | 
						|
---------------------------------
 | 
						|
 | 
						|
An image (class Image) is a container for a set of image levels (class
 | 
						|
ImageLevel), and an image level is a container for a set of image channels
 | 
						|
(class ImageChannel).  An image channel contains an array of pixel values.
 | 
						|
 | 
						|
For example:
 | 
						|
 | 
						|
    image --+-- level 0,0 --+-- channel "R" --- pixel data
 | 
						|
            |               |
 | 
						|
            |               +-- channel "G" --- pixel data
 | 
						|
            |               |
 | 
						|
            |               +-- channel "B" --- pixel data
 | 
						|
            |
 | 
						|
            +-- level 1,1 --+-- channel "R" --- pixel data
 | 
						|
            |               |
 | 
						|
            |               +-- channel "G" --- pixel data
 | 
						|
            |               |
 | 
						|
            |               +-- channel "B" --- pixel data
 | 
						|
            |
 | 
						|
            +-- level 2,2 --+-- channel "R" --- pixel data
 | 
						|
                            |
 | 
						|
                            +-- channel "G" --- pixel data
 | 
						|
                            |
 | 
						|
                            +-- channel "B" --- pixel data
 | 
						|
 | 
						|
An image has a level mode (enum LevelMode), which can be ONE_LEVEL,
 | 
						|
MIPMAP_LEVELS or RIPMAP_LEVELS.  A ONE_LEVEL image contains only a single
 | 
						|
level, but a multi-resolution image, that is, one with level mode set to
 | 
						|
MIPMAP_LEVELS or RIPMAP_LEVELS, contains multiple levels.  The levels are
 | 
						|
analogous to the levels in an OpenEXR file, as described in the "Technical
 | 
						|
Introduction to OpenEXR" document.
 | 
						|
 | 
						|
Levels are indexed by a pairs of level numbers.  Level (0,0) contains the
 | 
						|
highest-resolution version of the image; level (lx,ly) contains an image
 | 
						|
whose resolution is reduced in x and y by a factor of 2^lx and 2^ly
 | 
						|
respectively.  The level has a data window that indicates the range of
 | 
						|
x and y for which pixel data are stored in the level.
 | 
						|
 | 
						|
All levels in an image have the same set of image channels.
 | 
						|
 | 
						|
An image channel has a name (e.g. "R", "Z", or "xVelocity"), a type (HALF,
 | 
						|
FLOAT or UINT) and x and y sampling rates.  A channel stores samples for
 | 
						|
a pixel if the pixel is inside the data window of the level to which the
 | 
						|
channel belongs, and the x and y coordinates of the pixel are divisible by
 | 
						|
the x and y sampling rates of the channel.
 | 
						|
 | 
						|
An image can be either flat or deep.  In a flat image each channel in each
 | 
						|
level stores at most one value per pixel.  In a deep image each channel in
 | 
						|
each level stores an arbitrary number of values per pixel.  As an exception,
 | 
						|
each level of a deep image has a sample count channel with a single value
 | 
						|
per pixel; this value determines how many values each of the other channels
 | 
						|
in the same level has at the same pixel location.
 | 
						|
 | 
						|
The Image, ImageLevel and ImageChannel classes are abstact base classes.
 | 
						|
Two sets of classes, one for flat images and one for deep images, are
 | 
						|
derived from the base classes.  The FlatImageChannel and DeepImageChannel
 | 
						|
classes, derived from ImageChannel, are themselves base classes for the
 | 
						|
templates TypedFlatImageChannel<T> and TypedDeepImageChannel<T>:
 | 
						|
 | 
						|
    Image -> FlatImage
 | 
						|
          -> DeepImage
 | 
						|
 | 
						|
    ImageLevel -> FlatImageLevel
 | 
						|
               -> DeepImageLevel
 | 
						|
 | 
						|
 | 
						|
    ImageChannel -> FlatImageChannel -> TypedFlatImageChannel<T>
 | 
						|
                 -> DeepImageChannel -> TypedDeepImageChannel<T>
 | 
						|
                 -> SampleCountChannel
 | 
						|
 | 
						|
Channel objects of type TypedFlatImageChannel<T> and TypedDeepImageChannel<T> 
 | 
						|
contain pixel values of type T, where T is either half, float or unsigned int.
 | 
						|
For convenience, the following typedefs are provided:
 | 
						|
 | 
						|
    typedef TypedFlatImageChannel<half>         FlatHalfChannel;
 | 
						|
    typedef TypedFlatImageChannel<float>        FlatFloatChannel;
 | 
						|
    typedef TypedFlatImageChannel<unsigned int> FlatUIntChannel;
 | 
						|
 | 
						|
    typedef TypedDeepImageChannel<half>         DeepHalfChannel;
 | 
						|
    typedef TypedDeepImageChannel<float>        DeepFloatChannel;
 | 
						|
    typedef TypedDeepImageChannel<unsigned int> DeepUIntChannel;
 | 
						|
 | 
						|
 | 
						|
File I/O
 | 
						|
--------
 | 
						|
 | 
						|
An Image object can be saved in an OpenEXR file with a single function call:
 | 
						|
 | 
						|
    saveImage ("foo.exr", myImage);
 | 
						|
 | 
						|
The saveImage() function automatically creates a flat or a deep image file,
 | 
						|
depending on the type of the image.  All channels and all image levels will
 | 
						|
be saved in the file.
 | 
						|
 | 
						|
Optionally an OpenEXR Header object can be passed to the saveImage() function;
 | 
						|
this allows application code save custom attributes in the file, and to control
 | 
						|
how the file will be compressed:
 | 
						|
 | 
						|
    Header myHeader;
 | 
						|
    myHeader.compression() = PIZ_COMPRESSION;
 | 
						|
    myHeader.pixelAspectRatio() = 1.5;
 | 
						|
 | 
						|
    saveImage ("foo.exr", myHeader, myImage);
 | 
						|
 | 
						|
Loading an image from an OpenEXR file also requires only one function call,
 | 
						|
either
 | 
						|
 | 
						|
    Image* myImage = loadImage ("foo.exr");
 | 
						|
 | 
						|
or
 | 
						|
 | 
						|
    Header myHeader;
 | 
						|
    Image* myImage = loadImage ("foo.exr", myHeader);
 | 
						|
 | 
						|
The application owns the image that is returned by the loadImage() call.
 | 
						|
It is the application's responsibility to delete the Image object.
 | 
						|
 | 
						|
The IlmImfUtil library also provides versions of the saveImage() and
 | 
						|
loadImage() functions that work only on flat images or only on deep images:
 | 
						|
 | 
						|
    saveFlatImage()
 | 
						|
    saveFlatScanLineImage()
 | 
						|
    saveFlatTiledImage()
 | 
						|
    saveDeepImage()
 | 
						|
    saveDeepScanLineImage()
 | 
						|
    saveDeepTiledImage()
 | 
						|
 | 
						|
For details the the ImfFlatImageIO.h and ImfDeepImageIO.h header files.
 | 
						|
 | 
						|
 | 
						|
Manipulating Images in Memory
 | 
						|
-----------------------------
 | 
						|
 | 
						|
Creating a mip-mapped flat image with two channels:
 | 
						|
 | 
						|
    FlatImage fimg (Box2i (V2i (0, 0), V2i (255, 255)),    // data window
 | 
						|
                    MIPMAP_LEVELS);                        // level mode
 | 
						|
 | 
						|
    fimg.insertChannel ("R", HALF);
 | 
						|
    fimg.insertChannel ("Z", FLOAT);
 | 
						|
 | 
						|
Creating a single-level deep image:
 | 
						|
 | 
						|
    DeepImage dimg (Box2i (V2i (0, 0), V2i (255, 255)),    // data window
 | 
						|
                    ONE_LEVEL);                            // level mode
 | 
						|
 | 
						|
    dimg.insertChannel ("R", HALF);
 | 
						|
    dimg.insertChannel ("Z", FLOAT);
 | 
						|
 | 
						|
Reading and writing pixels in level (2,2) of the mip-mapped flat image
 | 
						|
(note: a mip-mapped image contains only levels where the x and y level
 | 
						|
numbers are equal.  For convenience, mip-map levels can be addressed
 | 
						|
using a single level number):
 | 
						|
 | 
						|
    FlatImageLevel &level = fimg.level (2);
 | 
						|
    FlatHalfChannel &R = level.typedChannel<half> ("R);
 | 
						|
 | 
						|
    half r1 = R.at (20, 15);    // read pixel (20,15), with bounds checking
 | 
						|
                                // (exception for access outside data window)
 | 
						|
 | 
						|
    half r2 = R (17, 4);        // read pixel (17,4) without bounds checking
 | 
						|
                                // faster, but crashes for access outside
 | 
						|
                                // data window
 | 
						|
 | 
						|
    R.at (20, 15) = 2 * r1;     // change pixel value, with and
 | 
						|
    R (17, 4) = 2 * r2;         // without bounds checking
 | 
						|
 | 
						|
Reading and writing pixels in the single-level deep image:
 | 
						|
 | 
						|
    DeepImageLevel &level = dimg.level();
 | 
						|
    DeepHalfChannel &R = level.typedChannel<half> ("R);
 | 
						|
 | 
						|
    // with bounds checking
 | 
						|
 | 
						|
    unsigned int n1 = R.sampleCounts().at (20, 15);
 | 
						|
    half r1;
 | 
						|
 | 
						|
    if (n1 > 0)
 | 
						|
        r1 = R.at(20, 15)[n1 - 1];  // read the last sample in pixel (20,15)
 | 
						|
 | 
						|
    // without bounds checking
 | 
						|
 | 
						|
    unsigned int n2 = R.sampleCounts()(20, 15);
 | 
						|
    half r2;
 | 
						|
 | 
						|
    if (n > 0)
 | 
						|
        r2 = R(17, 4)[n2 - 1];     // read the last sample in pixel (17,4)
 | 
						|
 | 
						|
    // change the value of an existing sample
 | 
						|
 | 
						|
    if (n1 > 0)
 | 
						|
        R(20,15)[n1 - 1] = r1 * 2;
 | 
						|
 | 
						|
    // append a new sample to a pixel and set the sample to 3.0
 | 
						|
 | 
						|
    R.sampleCounts().set (20, 15, n1 + 1);
 | 
						|
    R.(20, 15)[n1] = 3.0;
 | 
						|
 | 
						|
In addition to functions for reading and writing individual pixels, there
 | 
						|
are functions for accessing a whole row of pixels with a single function
 | 
						|
call.  For details see the ImfFlatImageChannel.h, ImfDeepImageChannel.h
 | 
						|
and ImfSampleCountChannel.h header files in the IlmImf library:
 | 
						|
 | 
						|
    T*                   TypedFlatImageChannel<T>::row (int r);
 | 
						|
    const T*             TypedFlatImageChannel<T>::row (int r) const;
 | 
						|
 | 
						|
    T * const *          TypedDeepImageChannel<T>::row (int r);
 | 
						|
    const T * const *    TypedDeepImageChannel<T>::row (int r) const;
 | 
						|
    const unsigned int * SampleCountChannel::row (int r) const;
 | 
						|
 | 
						|
To change the number of samples in all pixels in one row of a deep image
 | 
						|
level, use:
 | 
						|
 | 
						|
    void SampleCountChannel::set (int r, unsigned int newNumSamples[]);
 | 
						|
 | 
						|
Use an Edit object to temporarily make all sample counts in a deep image
 | 
						|
level editable:
 | 
						|
 | 
						|
    class SampleCountChannel::Edit;
 | 
						|
 | 
						|
Miscellaneous Functions:
 | 
						|
------------------------
 | 
						|
 | 
						|
Change the data window and the level mode of an image (pixel data are not
 | 
						|
preserved across the call):
 | 
						|
 | 
						|
    void Image::resize (const Box2i &dataWindow, LevelMode levelMode);
 | 
						|
 | 
						|
Shift the data window in x and y; shift the pixels along with the data window:
 | 
						|
 | 
						|
    void Image::shiftPixels (int dx, int dy);
 | 
						|
 | 
						|
Erase a channel, rename a channel, rename multiple channels at the same time:
 | 
						|
 | 
						|
    void Image::eraseChannel (const string &name);
 | 
						|
    void Image::renameChannel (const string &oldName, const string &newName);
 | 
						|
    void Image::renameChannels (const RenamingMap &oldToNewNames);
 | 
						|
 | 
						|
Missing Functionality:
 | 
						|
----------------------
 | 
						|
 | 
						|
At this point, the IlmImfUtil library cannot read or write multi-part
 | 
						|
files.  A future version of the library should probably define a new class
 | 
						|
MultiPartImage that contains a set of regular images.  The library should
 | 
						|
also define corresponding loadMultiPartImage() and saveMultiPartImage()
 | 
						|
functions.
 | 
						|
 | 
						|
Sample Code
 | 
						|
-----------
 | 
						|
 | 
						|
See the exrsave, exrmakescanlines, exrclip utilities.
 | 
						|
 | 
						|
 |