Loading and Saving Raster Images

Table Of Contents

Overview of Loading and Saving Raster Images

Images are loaded and saved by the IMG_File class, which provides a variety of features:

The easiest way to load an image file is to open() it, readImages() from it, and then close() the file.

   UT_PtrArray<PXL_Raster *> images;

   IMG_File *file = IMG_File::open("Snail.pic");
   if(file)
   {
       bool success = file->readImages(images);
       file->close();
       delete file;

       if(success)
       {
           // use rasters in images, and free them when you're done.
       }
   }

The following sections describe this process in more detail.

Opening Image Files

To open an image for reading, pass the filename to IMG_File::open(). You can also pass in an IMG_FileParms structure to tell the loader how to organize your data. By using the IMG_FileParms correctly, you may be able to avoid the need to post-process the scanline data. Not all the parameters are available for writing, since most of the data for the write is specified in the IMG_Stat. See the "Write Effect" section in each for what applies to writing and what is ignored.

IMG_FileParms allows you to do the following:

Data Organization

Raster depth conversion and RGB/RGBA conversions are common re-organizations. Now, you can also specify if you want the data interleaved (RGBRGBRGB) or non-interleaved (RRRGGGBBB) or as-is.

By default, the data is interleaved and left at the native raster depth and color model.

    // convert the data type (default = use native)
    void   setDataType(IMG_DataType dt);

    // convert to a standard color model (RGB, RGBA, single). Each plane 
    // will be this color model.
    void   setColorModel(IMG_ColorModel cm);

    // Determines how to format the data.
    //   IMG_INTERLEAVE_AS_IS  - leave it interleaved or non, as in the file.
    //   IMG_INTERLEAVED       - always interleave (rgbrgbrgb). Default.
    //   IMG_NON_INTERLEAVED   - always non-interleaved (rrrgggbbb)
    //
    void   setInterleaved(IMG_Interleave i);

    // If the color model is set to IMG_1CHAN, and the actual color model
    // is RGB or higher, this method determines how to convert the vector
    // into a scalar. By default, the luminance is taken.
    //
    void   setLuminanceFunc(PXL_LumFunction f);

    // If true, alpha will be read into its own plane, instead of an RGBA
    // color plane. Color will be read as its own plane as well, RGB.
    //
    void   readAlphaAsPlane();

    // If demoting from a deep raster to an RGB(A) image, these methods
    // allow you to specify the plane(s) to copy to RGB(A), by name or index.
    //

    // Indices are specified from 1 to #planes.
    //
    void   selectPlane(const UT_IntArray &planeindices);

    // Selects by numeric pattern, for example "1", "1 3 4", "[1-3] 2"
    //
    void   selectPlane(const char *pattern);

    // Selects by plane name pattern, such as "C", "C A Pz" "P? C*"
    //
    void   selectPlaneName(const char *name);

Write Effect

These are the effects of the above IMG_FileParms methods when writing:

Resolution

In addition to scaling to a given resolution, the new API also supports some more common scale operations. Most of these are mutually exclusive. To do cropping instead of scaling, see Data Window below.

By default, the native resolution is used.

    // scale the image to resolutuion (x,y). (0 = use orginal dimension)
    void   scaleImageTo(int x, int y, UT_FilterType ft=UT_FILTER_BOX);

    // scale the image by scaling factors (x,y).
    void   scaleImageBy(fpreal x, fpreal y, UT_FilterType ft=UT_FILTER_BOX);

    // limit the image to a maximum resolution. Scale to this res, preserving
    // the aspect ratio.
    void   limitImageRes(int x, int y, UT_FilterType ft=UT_FILTER_BOX);

    // images must be read as powers of two. Does not preserve aspect.
    void   powerTwoOnly();

Write Effect

ignored for all.

Data Window

Data windows can be embedded in certain file formats, but you can also specify your own window (regardless of whether a data window exists in the file). When specifying a subregion to read, you will be reading that area, not the full image area.

By default, all file data windows are expanded to fill the entire image.

    // normally, a data window is expanded or cropped to the image resolution.
    // calling this will always read only the data window.
    //
    void   setDataWindowOnly();

    // read the image region in 'area' only (even if we need to crop or expand
    // the image to fill it)
    void   setWindow(const UT_DimRect &area);

    // fills the data outside the window with bgcol. If reading, this overrides
    // any outside behaviour embedded in the file.
    //
    void   setWindowOutsideColor(fpreal bgcol[4]);

    // fills the data outside the window by streaking the edges outward to
    // fill the area.
    //
    void   setWindowStreak();

Write Effect

ignored for all.

Orientation and Flipping

In addition to being able to flip a file vertically, the new API allows you to specify an orientation and then flip horizontally or vertically. You can also flop the image (rotate it 90'). The orientation and flip work together to avoid doubling flipping.

By default, the image is read so that the (0,0) pixel is in the bottom left corner of the image.

    // options for orienting and flipping the image. Default orienation is
    // LEFT_FIRST, BOTTOM_FIRST. You can set each to 'none' if you don't care.
    //
    void   orientImage(IMG_XOrientation x, IMG_YOrientation y);

    void   flipImageVertical();
    void   flipImageHorizontal();

    // rotate the image 90', flopping it on its side.
    void   flopImage();

Write Effect

Specifies how to flip the image when outputting. flopImage() ignored.

Color Correction

You can also specify color correction to be applied to the image before any of its data is converted (ie, if you're converting a FP image to an 8 bit texture, you can apply gamma and/or a LUT first, before quantization occurs).

By default, no color correction is performed.

    // apply a lookup table to the data, but only those planes that match the
    // scope.
    //
    void   applyLUT(const char *lut, const char *plane_scope = "*");

    // Apply gamma to the planes matching the scope.
    //
    void   applyGamma(fpreal gamma, const char *gamma_scope = "*");

Write Effect

Applies a LUT and gamma to the output planes which match the scope.

Format Options

You can set format-specific options, like compression, through the IMG_FileParms::setOption() method. This is still relatively simplistic, as it takes a string for both option name and value. This may be upgraded later.

Reading or Writing Tiles

You can use a tile-based interface to IMG_File for writing data as well. Internally, this still uses scanlines. This is a convenience function for ease of data manipulation only.

    // if set, we're reading or writing tiles with readTile() and writeTile()
    // instead of readScanline() and writeScanline(). The area of each tile may
    // vary per call, but no pixel may be written twice for the same plane.
    void   useTileInterface();

Applying the Image Options

Once you have created your IMG_FileParms structure, pass it to the IMG_File::open() method:

   IMG_FileParms fparms;

   fparms.scaleImageTo(100,100);
   fparms.setInterleaved(IMG_NON_INTERLEAVED);
   fparms.useTileInterface();

   IMG_File *fp = IMG_File::open(filename, &fparms);
   if(fp) ...

The IMG_FileParms structure is optional in the open() call. If you don't specify it, by default, the file reader is set up to read a full interleaved xres x yres image in its native data format, oriented bottom first.

Note that the object you get back may not be of the type you are expecting - it could be a translator object. If the open() failed, a NULL pointer is returned.

Querying the Image

Once the file has successfully been opened, you can query the image structure in the IMG_Stat of the file, using fileptr->getStat(). This structure still contains the resolution and pixel aspect ratio, but the data type and color models have been moved into the IMG_PlaneInfo classes, which IMG_Stat contains a list of (one per plane). Each plane has a name, size and data format.

The Data Window has also moved from IMG_File to IMG_Stat. To check the dimensions of the area you will be reading, always call getDataWidth() and getDataHeight(), not getXres() and getYres(). The size of the full scanline (including all planes) and each plane's scanline can be returned through bytesPerScanline() and bytesPerPlaneScan().

To allocate buffer space for any scanline, you can use IMG_File::allocScanlineBuffer(). This will allocate a buffer big enough to handle any plane by itself.

Reading the Image using Scanlines

To read a scanline, call read() with the scanline number and IMG_PlaneInfo pointer that you want to read from. read() will return a pointer to the data, which you can index into. Do not delete this data! If the read() call returns NULL, an error has occurred while reading the scanline and you should stop reading immediately.

To copy the data into a buffer you are providing, you can call readIntoBuffer() instead (this method returns true for success and false for failure). The buffer must be big enough to fit the scanline, as reported by myStat.bytesPerPlaneScan().

Regardless of where a data window or subregion is in the image, scanlines are always indexed from 0 to getStat().getDataHeight()-1. If you want to read scanlines in random order, you should call randomReadAccessRequired() on the IMG_File object first. Otherwise, you should read the scanlines in ascending order.

If you are reading more than one plane, but you want to read a plane entirely before starting another plane, you must call randomReadAccessRequired() as well.

Reading the Image using PXL_Rasters

Often images are read entirely into memory. To do this, call IMG_File::readImages() with an empty UT_PtrArray of PXL_Raster's. The readImages() method will add one PXL_Raster per plane in the image (usually one image for a normal format, and many rasters for a deep raster format). These PXL_Rasters need to be freed once you are finished using them.

Note:
If there is more than one PXL_Raster in the list, they may differ in data format or color model. The resolution will remain constant across the images.

Writing the Image using Scanlines

Writing the image is similar to reading, except that you need to define the IMG_Stat structure before opening the file. This structure defines the image's characteristics, such as resolution, data format, and plane composition. Call IMG_File::create() to open an image file for writing.

Once you have opened the file, unlike reading, you must write every scanline in the file, for each plane you have defined. If you do not, the planes you did not define will contain garbage data. Unlike reading, you can write scanlines in any order (though it is generally most efficient to write them in ascending order).

Reading the Image using PXL_Rasters

Writing full images is done almost completely opposite to reading them. You provide the IMG_File::writeImages() method with a list of PXL_Rasters containing your image data. The number of rasters needs to match the number of planes in the specified IMG_Stat, and the data formats, color models and resolutions should also match. You can set the optional freerasters parameter to free the rasters for you once the write has completed.

Closing the File

When finished reading or writing, close the file by calling file->close(). Don't forget to delete the file object.

FAQ

Q: What happens if I try to write multiple planes to a format that doesn't support them (like jpg)?

A: The default plane is chosen (C or plane 0).

Q: What happens if I'm writing a deep raster and the format doesn't support different formats for each image?

A: The highest bit depth format is chosen from the planes, so that all planes can be represented properly.

Q: I am writing a file and specified a data window on a format that doesn't support it. What happens?

A: The writer expands the data window to the actual resolution of the image, filling in the outside area with black.

Q: The data type I'm writing doesn't match any of the types that the format supports. What happens?

A: The best data format the file provides is matched to the format you specified, and the data is converted. For example, if you write FP data to an SGI file, which only supports 8 and 16 bit ints, the 16bit int format is chosen and the data clamped at [0,1]. If you wrote 8bit int to a .exr file, it would be converted to 16bit fp.

Q: I don't want the file writer to make automatic decisions about fitting my data. How can I determine what a certain format will support?

A: Call IMG_Format::findFormat(filename) to get the IMG_Format description of that format. You can then query a lot of the this API's features:

    virtual IMG_DataType        getSupportedTypes() const;
    virtual IMG_ColorModel      getSupportedColorModels() const;
    virtual void                 getMaxResolution(unsigned &x,unsigned &y) const;

    virtual int                  isReadRandomAccess() const;
    virtual int                  isWriteRandomAccess() const;

    // data orientation.
    virtual int                  isTopFirst() const;
    virtual int                  isLeftFirst() const;

    // data organization.
    virtual bool                 isDataWindowSupported() const;
    virtual bool                 isDeepRasterSupported() const;
    virtual IMG_DeepRasterColor  getDeepRasterRGBASupported() const;
    virtual bool                 isDataInterleaved() const;

    // if true, planes can have different data formats & components.
    virtual bool                 canPlaneTypesDiffer() const;

Using IMG_Raster to Load and Save Images

Although IMG_Raster is a deprecated class, it can be used to quickly save and load a raster image.
    stat(const char *filename, short *xres, short *yres)
    load(const char *filename, int xres, int yres, ...)
    save(const char *filename)

The reason that these methods are obsolete is that they only handle 8 and 16 bit color channels. 32b and 16b floating point images will be clipped to 16b fixed color.

On success, the above methods will return 1. If they fail, the return value will be 0.

The load methods will determine the correct format type by looking at the extension of the filename. Any format supported by Houdini may be saved or loaded. If the extension doesn't map to any known suffix, Alias format is used for saving (this does not include an alpha channel).

See the SOP_CopRaster example (SOP/SOP_CopRasterC) for an example of using the IMG_Raster::load method.


Generated on Mon Jan 28 00:27:57 2013 for HDK by  doxygen 1.5.9