2.1 NCSA HDF Calling Interfaces and Utilities Storing Raster Images 2.1 National Center for Supercomputing Applications March 1993 2.1 NCSA HDF Calling Interfaces and Utilities Storing Raster Images 2.1 National Center for Supercomputing Applications March 1993 Chapter 2 Storing Raster Images Chapter Overview Header Files Raster Image Sets Reasons to Use Raster Image Sets 8-Bit Raster Image Sets Compression Schemes Writing 8-Bit Raster Images to a File Reading 8-Bit Raster Images from a File 24-Bit Raster Image Sets Interlace Schemes Compression Schemes Writing 24-Bit Raster Images to a File Reading 24-Bit Raster Images from a File Examples Involving 24-bit Raster Image Sets Sample Programs A C Program to Convert a Raw Palette and Raw Raster Image to HDF RIS8 Format C Functions to Convert Floating-Point Data to 8-Bit Raster Data Chapter Overview This chapter discusses the purposes and use of raster image sets, which allow you to store an image, together with its dimensions and a palette, in an HDF file. This chapter specifically introduces and describes the two raster image set interfaces currently contained in the HDF library: RIS8 and RIS24. Header Files The header file hdf.h contains the declarations and definitions that are used by the routines listed in this chapter. This file can, if needed, be included with your C source code, and in some cases also with FORTRAN code. Raster Image Sets A raster image set (RIS) is a set of tags and associated information required to store an image in an HDF file. In HDF, 8-bit raster image sets (RIS8) are used to store 8-bit raster images, and 24-bit raster image sets (RIS24) are used to store 24-bit raster images. The HDF library currently contains routines for storing raw raster images in RIS8 or RIS24 format and for retrieving raster images from files containing raster image sets. These routines are callable from C and FORTRAN programs that have access to the library. Table 2.1 lists the long and short names and the functions of the RIS8 and RIS24 routines currently contained in the HDF library. The following sections provide descriptions and examples of these calling routines. Table 2.1 Raster Image Set Routines in the HDF Library FORTRAN C Name Name Function DFR8setpalette d8spal sets the default palette to be used for subsequent images. DFR8addimage d8aimg appends the RIS8 for the image to the file. DFR8putimage d8pimg writes out the RIS8 for the image as the first image in the file. DFR8writeref d8wref sets the reference number of the image to write next DFR8getdims d8gdims retrieves the dimensions of the image and indicates whether a palette is associated and stored with the image. DFR8getimage d8gimg retrieves the image and any associated palette, and stores them in arrays. DFR8readref d8rref sets the reference number of the image to get next DFR8restart d8first causes the next get command to read from the first RIS8 in the file DFR8nimages d8nims counts the number of images stored in the file (FORTRAN version currently not available) DFR8lastref d8lref returns reference number of last RIS8 read or written DF24setil d2setil sets the interlace to be used when writing out the RIS24 for the image. DF24addimage d2aimg appends the RIS24 for the image to the file. DF24putimage d2aimg writes out RIS24 for the image to the file. DF24getdims d2gdims retrieves the dimensions and interlace of the image. DF24getimage d2gimg retrieves the image and stores it in an array. DF24readref d2rref sets the reference number of the image to get next DF24restart d2first causes the next get command to read from the first RIS24 in the file, rather than the next one. DF24reqil d2reqil specifies an interlace to be used in place of the interlace indicated in the file when the next raster image is read. Reasons to Use Raster Image Sets When raster images are stored in the form of HDF raster image sets, it becomes possible to use a variety of software tools for displaying and manipulating them. NCSA Image, for instance, can operate directly on images stored in HDF raster image format. Other software can display raster images in HDF format on a variety of different machines. A Raster Image Set (RIS) is a collection of information related to a raster image. A RIS can include such information as the dimensions, compression scheme, and interlacing scheme use for a particular image, along with the image itself. When this information is stored together in a file with an image, software does not have to search elsewhere for this pertinent information. This reduces the need to coordinate disparate pieces of information about a raster image, making the job of creating and running image-processing programs significantly easier. 8-Bit Raster Image Sets The phrase, 8-bit raster image set (RIS8), refers to the set of tags and associated information required to store an 8-bit raster image in an HDF file. An RIS8 contains at least the first three of the following components and may also contain a palette: * An imageÑhere, a two-dimensional array of 8-bit numbers, one for each pixel in the raster image, where pixel values range from 0 to 255 (Pixel values indicate to the hardware which colors to use when drawing the corresponding pixels on the screen.) * DimensionsÑtwo values that represent the x and y dimensions of the image, respectively * A compression schemeÑa code that indicates if and how the image was compressed (See the following section, "Compression Schemes.) * A paletteÑa lookup table with 256 entries that tells the color to associate with each of the 256 possible pixel values (Each entry in the palette is chosen from a master palette of 224 RGB colors. Each palette entry consists of three bytes, one each for red, green, and blue. The first three bytes represent the R, G, and B values of the first color in the palette; the next three the R, G, and B values of the second color; and so forth. The total size of a palette is 768 bytes.) An example of an HDF file with two raster image sets is illustrated in Figure 2.1. ED. NOTE: Figures are not available in this plain text version of the specification. Figure 2.1 Two Raster Image Sets in an HDF File Compression Schemes A compression scheme indicates if and how an image is compressed. Compression schemes currently supported by NCSA HDF are run length encoding and IMCOMP. The value of the integer argument compress in DFR8putimage and DFR8addimage determines which scheme, if any, is to be used, as shown in Table 2.2. Table 2.2 Compression Scheme Codes Value Compression Scheme 0 none DFTAG_RLE run length encoding (RLE) DFTAG_IMCOMP IMCOMP The HDF tags DFTAG_RLE and DFTAG_IMCOMP are defined as the values 11 and 12, respectively, in the file 'hdf.h'. You can avoid using numbers for compression codes if you include this file in your program. A Note About RLE The run length encoding (RLE) method used in HDF works as follows: Each sequence of pixels begins with a count byte. The low seven bits of the count byte indicate the number of bytes in the sequence (n). The high bit of the count byte indicates whether the next byte should be replicated n times (high bit=1), or whether the next n bytes should be included as is (high bit=0). The amount of space saved by RLE depends upon how much repetition there is among the pixels in the rows. (Pixels are stored in rows.) If there is a great deal of repetition, much space is saved; if there is little repetition, the savings can be very small. In the worst caseÑwhen every pixel is different from the one that precedes itÑan extra byte is added for every 127 bytes in the image. A Note About IMCOMP IMCOMP should be used with caution if you are concerned about losing information in your image. IMCOMP compression first breaks an image into 4 x 4 arrays of pixels, then for each array chooses two colors to distribute in the array. (These two colors are added to a 256-color palette that IMCOMP compression builds. This new palette is based on, but different from, the original palette assigned.) Each of the 16 pixels in the 4 x 4 array can now be represented by one bit (0=first color; 1=second color). In addition to these sixteen bits, there are two bytes that give the palette locations of the two colors that were assigned to the 4 x 4 array. Since each 4 x 4 array uses only 4 bytes, IMCOMP stores an image at a cost of 2 bits per pixel, which is 25% of the original storage requirement for the 8-bit image. The drawback of this savings is loss of informationÑonly two colors are allowed to occupy each 4 X 4 array of pixelsÑwhereas in the original image, 16 colors could occupy the same space. For many images this cost is bearable and hardly noticeable, but for some images, the results can be totally unrecognizable. Also note that IMCOMP is dependent on the existence of a palette. If you are going to use IMCOMP, you must include a palette with your image. Writing 8-Bit Raster Images to a File This section contains descriptions of routines supported in the HDF library for writing 8-bit RIS to HDF files. DFR8setpalette FORTRAN: INTEGER FUNCTION d8spal(palette) CHARACTER*1 palette(768) - palette to go with image C: int DFR8setpalette(palette) uint8 palette[768]; /* palette to go with image */ Purpose: To indicate what palette, if any, is to be used for subsequent images. Returns: 0 on success; -1 on failure. The palette that is set here continues as the default palette until it is changed by a new call to the routine. DFR8putimage FORTRAN: INTEGER FUNCTION d8pimg(filename, image, width, height, compress) CHARACTER*(*)filename - name of file to store RIS8 in INTEGER width, height - dimensions of image CHARACTER image(width,height) - array holding image to be put in file INTEGER compress - type of compression to use, if any C: int DFR8putimage(filename, image, width, height, compress) char *filename; /* name of file to store RIS8 in */ int32 width, height; /* dimensions of image */ char image[height][width]; /* array with image */ /* to put in file */ uint16 compress; /* type of compression to use, if any */ Purpose: To write out the RIS8 for the image as the first image in the file, overwriting any information that was previously in the file. Returns: 0 on success; -1 on failure. The argument compress identifies the scheme to be used for compressing the data, if any. Refer to Table 2.2 for valid values of compress. If IMCOMP compression is used, the image must include a palette. (See the discussion of 8-bit compression schemes in the section, "Compression Schemes.") NOTE: DFR8putimage overwrites any information that exists in the HDF file. To write an image to a file by appending it, rather than overwriting it, use DFR8addimage (see below) . NOTE: In FORTRAN, the dimensions of the array image must be the same as the dimensions of the image itself. NOTE: The order in which you declare dimensions is different between C and FORTRAN. Ordering varies because FORTRAN arrays are stored in column-major order, while C arrays are stored in row-major order. (Row-major order implies that the horizontal coordinate varies fastest). When DFR8putimage writes an image to a file, it assumes row-major order. The FORTRAN declaration that causes an image to be stored in this way must have the width as its first dimension and the height as its second dimension, the reverse of the way it is done in C. To take this into account as you build your image in your FORTRAN program, you need to build the image "on its side." DFR8addimage FORTRAN: INTEGER FUNCTION d8aimg(filename,image,width,height, compress) CHARACTER*(*) filename - name of file to add RIS8 to CHARACTER image(width,height) - array holding image to be added to file INTEGER width, height - dimensions of the image INTEGER compress - type of compression to use, if any C: int DFR8addimage(filename,image,width,height,compress) char *filename; /* name of file to add RIS8 to */ char image[height][width]; /* array holding image to add to file */ int32 width, height; /* dimensions of the image */ uint16 compress; /* type of compression to use, if any */ Purpose: To append to the file the RIS8 for the image. Returns: 0 on success; -1 on failure. In all other respects, DFR8addimage is functionally equivalent to DFR8putimage. DFR8writeref FORTRAN: INTEGER FUNCTION DFR8wref(name, ref) character*(*) name - name of file containing image integer ref - reference number for next d8pimg or d8aimg C: int DFR8writeref(filename, ref) char *filename; /* file containing image */ uint16 ref; /* reference number for next DFR8putimage or DFR8addimage */ Purpose: To specify the reference number of the image to be written when DFR8addimage or DFR8putimage is next called. Returns: 0 on success; -1 on failure. CAUTION: It is unlikely that you will need this routine, but if you do, use it with caution. There is no guarantee that reference numbers appear in sequence in an HDF file; therefore, it is not safe to assume that a reference number is the sequence number for an image. Example: Writing a Palette and an Image in RIS8 Format Figure 2.2 demonstrates in FORTRAN how a palette stored in the array colors and a 400x600 (height=400, width=600) raw image stored in the array picture are written to a file in RIS8 format. The image is not compressed. Figure 2.2 Storing an RIS8 FORTRAN: INTEGER d8spal, d8pimg CHARACTER*1 colors(768) CHARACTER*1 picture(600,400) INTEGER ret ... ret = d8spal(colors) ret = d8pimg('myfile.hdf',picture,600,400,0) if (ret .ne. 0)write(*,*) 'Error writing image.' ... The RIS8 with this palette and image is stored as the first image in 'myfile.hdf'. Note that if something already existed in this file, it will be lost, because d8pimg recreates the file. If you simply want to append an image to the file, use d8aimg. Example: Writing a Series of RIS8 Images Figure 2.3 illustrates a series of FORTRAN calls in which four 800 x 1200 (height=800; width=1200) images are written to the same file. The first two use palette palA and are compressed using the run length encoding technique (DFTAG_RLE); the third and fourth use palette palB and are not compressed. Figure 2.3 Storing Multiple RIS8s in a Single File FORTRAN: INTEGER d8spal, d8pimg, d8aimg CHARACTER*1 palA(768), palB(768) CHARACTER*1 pic1(1200,800), pic2(1200,800) CHARACTER*1 pic3(1200,800), pic4(1200,800) INTEGER ret, DFTAG_RLE PARAMETER (DFTAG_RLE = 11) ... ret = d8spal(palA) ret = d8pimg('myfile',pic1,1200,800,DFTAG_RLE) ret = d8aimg('myfile',pic2,1200,800,DFTAG_RLE) ret = d8spal(palB) ret = d8aimg('myfile',pic3,1200,800,0) ret = d8aimg('myfile',pic4,1200,800,0) ... Reading 8-Bit Raster Images from a File The two routines, DFR8getdims and DFR8getimage, are sufficient to read raster images from a file. If enough is known about the images and palettes, only the latter routine is needed. DFR8getdims FORTRAN: INTEGER FUNCTION d8gdims(filename,width,height,ispalette) CHARACTER*(*) filename - name of file with RIS8 image INTEGER width, height - dimensions of next image in file INTEGER ispalette - 1 if there is a palette, else 0 C: int DFR8getdims(filename,width,height,ispalette) char *filenDFR8putimage writes an image to a file, it assumes row-major order. The FORTRAN declaration that causes an image to be stored in this way must have the width as its first dimension and the height as its second dimension, the reverse of the way it is done in C. To take this into account as you build your image in your FORTRAN program, you need to build the image "on its side." DFR8addimage FORTRAN: INTEGER FUNCTION d8aimg(filename,image,width,height, compress) CHARACTER*(*) filename - name of file to add RIS8 to CHARACTER image(width,height) - array holding image to be added to file INTEGER width, height - dimensions of the image INTEGER compress - type of compression to use, if any C: int DFR8addimage(filename,image,width,height,compress) char *filename; /* name of file to add RIS8 to */ char image[height][width]; /* array holding image to add to file */ int32 width, height; /* dimensions of the image */ uint16 compress; /* type of compression to use, if any */ Purpose: To append to the file the RIS8 for the image. Returns: 0 on success; -1 on failure. In all other respects, DFR8addimage is functionally equivalent to DFR8putimage. DFR8writeref FORTRAN: INTEGER FUNCTION DFR8wref(name, ref) character*(*) name - name of file containing image integer ref - reference number for next d8pimg or d8aimg C: int DFR8writeref(filename, ref) char *filename; /* file containing image */ uint16 ref; /* reference number for next DFR8putimage or DFR8addimage */ Purpose: To specify the reference number of the image to be written when DFR8addimage or DFR8putimage is next called. Returns: 0 on success; -1 on failure. CAUTION: It is unlikely that you will need this routine, but if you do, use it with caution. There is no guarantee that reference numbers appear in sequence in an HDF file; therefore, it is not safe to assume that a reference number is the sequence number for an image. Example: Writing a Palette and an Image in RIS8 Format Figure 2.2 demonstrates in FORTRAN how a palette stored in the array colors and a 400x600 (height=400, width=600) raw image stored in the array picture are written to a file in RIS8 format. The image is not compressed. Figure 2.2 Storing an RIS8 FORTRAN: INTEGER d8spal, d8pimg CHARACTER*1 colors(768) CHARACTER*1 picture(600,400) INTEGER ret ... ret = d8spal(colors) ret = d8pimg('myfile.hdf',picture,600,400,0) if (ret .ne. 0)write(*,*) 'Error writing image.' ... The RIS8 with this palette and image is stored as the first image in 'myfile.hdf'. Note that if something already existed in this file, it will be lost, because d8pimg recreates the file. If you simply want to append an image to the file, use d8aimg. Example: Writing a Series of RIS8 Images Figure 2.3 illustrates a series of FORTRAN calls in which four 800 x 1200 (height=800; width=1200) images are written to the same file. The first two use palette palA and are compressed using the run length encoding technique (DFTAG_RLE); the third and fourth use palette palB and are not compressed. Figure 2.3 Storing Multiple RIS8s in a Single File FORTRAN: INTEGER d8spal, d8pimg, d8aimg CHARACTER*1 palA(768), palB(768) CHARACTER*1 pic1(1200,800), pic2(1200,800) CHARACTER*1 pic3(1200,800), pic4(1200,800) INTEGER ret, DFTAG_RLE PARAMETER (DFTAG_RLE = 11) ... ret = d8spal(palA) ret = d8pimg('myfile',pic1,1200,800,DFTAG_RLE) ret = d8aimg('myfile',pic2,1200,800,DFTAG_RLE) ret = d8spal(palB) ret = d8aimg('myfile',pic3,1200,800,0) ret = d8aimg('myfile',pic4,1200,800,0) ... Reading 8-Bit Raster Images from a File The two routines, DFR8getdims and DFR8getimage, are sufficient to read raster images from a file. If enough is known about the images and palettes, only the latter routine is needed. DFR8getdims FORTRAN: INTEGER FUNCTION d8gdims(filename,width,height,ispalette) CHARACTER*(*) filename - name of file with RIS8 image INTEGER width, height - dimensions of next image in file INTEGER ispalette - 1 if there is a palette, else 0 C: int DFR8getdims(filename,width,height,ispalette) char *filename; /* name of file with RIS8 image */ int32 *width, *height; /* dimensions of next image in file */ int *ispalette; /* 1 if there is a palette, else 0 */ Purpose: To open the file with name filename, find the next image, retrieve the dimensions of the image in width and height, and tell, via ispalette, whether there is a palette associated with the image. Returns: 0 on success; -1 on failure. If the file is being opened for the first time, DFR8getdims returns information about the first image in the file. If an image has already been read, DFR8getdims finds the next image. Thus, images are read in the same order in which they were written to the file. Normally, DFR8getdims is called before DFR8getimage so that if necessary, space allocations for the image and palette can be checked, and the dimensions can be verified. If this information is already known, DFR8getdims need not be called. DFR8getimage FORTRAN: INTEGER FUNCTION d8gimg(filename, image, bufwidth, bufheight, palette) CHARACTER*(*) filename - name of file with RIS8 image INTEGER bufwidth,bufheight - dimensions of the buffer allocated to store image CHARACTER*1 image(bufwidth,bufheight) - array that will hold image CHARACTER*1 palette(768) - palette to go with image C: int DFR8getimage(filename, image, bufwidth, bufheight, palette) char *filename; /* name of file with RIS8 image */ int32 bufwidth, bufheight; /* dimensions of the buffer allocated to store image */ uint8 image[bufheight][bufwidth]; /* array that will hold image */ uint8 palette[768]; /* palette to go with image */ Purpose: To retrieve the image and its palette, if it is present, and store them in the specified arrays. Returns: 0 on success; -1 on failure. If palette is NULL, no palette is loaded, even if there is one stored with the image. If the image in the file is compressed, DFR8getimage automatically decompresses it. If DFR8getdims has not been called, DFR8getimage finds the next image in the same way that DFR8getdims does. NOTE: The variables bufwidth and bufheight give the number of columns and rows, respectively, in the array which you've allocated in memory to store the image. The image may actually be smaller than the allocated space. NOTE: The order in which you declare dimensions is different between C and FORTRAN. Ordering varies because FORTRAN arrays are stored in column-major order, while C arrays are stored in row-major order. (Row-major order implies that the horizontal coordinate varies fastest). When d8gimg reads an image from a file, it assumes row-major order. The FORTRAN declaration that causes an image to be stored in this way must have the width as its first dimension and the height as its second dimension. To take this into account as you read image in your program, you need to read in the image "on its side." DFR8readref FORTRAN: INTEGER FUNCTION d8rref(name, ref) character*(*) name - name of file containing image integer ref - reference number for next d8gimg C: int DFR8readref(filename, ref) char *filename; /* file containing image */ uint16 ref; /* reference number for next DFR8getimage */ Purpose: To specify the reference number of the image to be read when DFR8getimage is next called. Returns: 0 on success; -1 on failure. This routine is most likely to be used in conjunction with DFANgetlablist1, which returns a list of labels for a given tag together with their reference numbers. It provides, in a sense, a random access to images. NOTE: There is no guarantee that reference numbers appear in sequence in an HDF file; therefore, it is not safe to assume that a reference number is the sequence number for an image. DFR8restart FORTRAN: INTEGER FUNCTION d8first() C: int DFR8restart() Purpose: To cause the next get to read from the first RIS8 in the file, rather than the RIS8 following the one that was most recently read. Returns: 0 on success; -1 on failure DFR8nimages FORTRAN: INTEGER FUNCTION d8nims(filename) CHARACTER*(*) filename - name of HDF file C: int DFR8nimages(filename) char *filename; /* name of HDF file */ Purpose: To count the number of 8-bit raster images contained in an HDF file. Returns: Number of images on success; -1 on failure. DFR8lastref FORTRAN: INTEGER FUNCTION d8lref() C: uint16 DFR8lastref() Purpose: To get last reference number written or read for an RIS8. Returns: Reference number on success; -1 on failure. This routine is primarily used for annotations. See Chapter 5, "Annotating Data Objects and Files," for examples. Example: FORTRAN Program to Read and Write a Raster Image Set Figure 2.4 shows a FORTRAN program that reads in an image from a file called old.hdf into an array called image. It is known that a palette exists. The program also writes out the image to a file called new.hdf. Figure 2.4 Reading an RIS8: Dimensions and Presence of Palette Known (FORTRAN) FORTRAN: program test_getimage C Program to illustrate use of d8gdims and d8gimg C C****||*************************************************** INTEGER d8gdims, d8gimg, d8aimg INTEGER ispal, ret, width, height CHARACTER*1 image(200,150), pal(768) C****||***************** read in image ******************** ret = d8gdims('old.hdf', width, height, ispal) if ( (width.eq.200) .and. (height.eq.150) ) then ret = d8gimg('old.hdf',image,width,height,pal) else print *, 'Wrong dimensions. Program aborted.' stop endif C****||***** write same image to different file ************ ret = d8aimg('new.hdf',image, width,height, 0) stop end Remarks: The raster image stored in the file is known to be 200 bytes wide and 150 bytes high. Because of the storage order used by FORTRAN, the program is loading the image "on its side." Hence, the declaration "CHARACTER*1 image(200,150)" rather than "CHARACTER*1 image(150,200)." Example: C Program to Read and Write a Raster Image Set Figure 2.5 shows a C program that reads in an image from a file called old.hdf into an array called image. It is known that a palette exists. The program also writes out the image to a file called new.hdf. Figure 2.5 Reading an RIS8: Dimensions and Presence of Palette Known (C) C: /* ** Program to illustrate use of DFR8getdims and DFR8getimage */ #include "hdf.h" #define HEIGHT 150 #define WIDTH 200 main() { int DFR8getdims(), DFR8getimage(), DFR8putimage(); int32 width, height; intn ispal, ret; char image[HEIGHT][WIDTH], pal[768]; /************* read in image *****************/ DFR8getdims("old.hdf", &width, &height, &ispal); if ( (width==WIDTH) && (height==HEIGHT) ) { DFR8getimage("old.hdf",image,width,height,pal); } else { printf("Wrong dimensions. Program aborted."); exit(1); } /****** write same image to different file ***********/ DFR8addimage("new.hdf", image, width, height, 0); } Example: FORTRAN Program to Read in a Raster Image Set, where Allocated Space is Larger than Image Figure 2.6 shows a FORTRAN program that reads in an image whose size is different from the size of the space allocated to hold the image. It then moves the image to a array that is of the correct size, and outputs the new image to a new HDF file. Figure 2.6 Reading an RIS8: Dimensions Different from Allocated Space FORTRAN: program big_buffer C Program using d8gimg when buffers are larger than image C C****||*************************************************** integer d8gdims, d8gimg integer width, height, ispal, ret character*1 image(500,400) character*1 newimage(200,150), pal(768) C****||****** read in image into "too-large" array ******** ret = d8gdims('old.hdf', width, height, ispal) ret = d8gimg('old.hdf', image,500, 400, pal) print *, 'width=',width,' height=',height C****||*** copy image to an array that is the "right size" *** do 100 i=1,200 do 100 j=1,150 newimage(i,j) = image(i,j) 100 continue C****||*** write newimage new file--same as original image *** ret = d8pimg('new.hdf',newimage,200,150,0) stop end Remarks: * The RIS8, stored in a file called, old.hdf, is read into an array called image. This array is deliberately made larger than the expected image. Figure 2.7 shows how the image fits inside the buffer. Note that since FORTRAN is used, the image is stored "on its side." * The array newimage is an array that is exactly the size of the image. After it is written to new.hdf, you can view the image in that file and see that it is identical to the original image. * Although it is possible, as this example illustrates, to read an image into a buffer that is larger than the image, the reverse is not possible. The RIS8 interface only supports writing an image from an array that was allocated to be exactly the same size of the image. Figure 2.7 FORTRAN Image Stored in Oversized Buffer 24-Bit Raster Image Sets The phrase 24-bit raster image set (RIS24) refers to the set of tags and associated information required to store a 24-bit raster image in an HDF file. An RIS24 contains at least the following components: * An imageÑhere, a two-dimensional array of 24-bit pixel representations, where each 24-bit pixel value has three 8-bit components: one each for the red, green, and blue (RGB) values of the pixel color. These RGB values may be arranged in the file in one of three different ways (see the following section, "Interlace Schemes"). * An interlace schemeÑa code that describes the order in which the pixel components are physically stored in the file (see the following section, "Interlace Schemes"). * DimensionsÑtwo values that represent the x and y dimensions of the image, respectively. Interlace Schemes An interlace scheme describes the way an image is stored in a file or in memory. NCSA HDF supports different interlace schemes because graphics applications and devices vary in the way they organize graphics images. By storing an image in a file using a scheme that is consistent with the expected application or device, you can achieve substantial improvements in performance. The value of the integer argument il determines which scheme is to be used, as shown in Table 2.3. The interlace schemes are described in the following sections. Table 2.3 Interlace Scheme Codes Value of il Interlace Scheme 0 pixel 1 scan-line 2 scan-plane Pixel Interlace Scheme The default interlace scheme describes an image pixel-by-pixel. This scheme is called pixel interlace. The code to specify the pixel interlace scheme in an RIS24 is 0 (zero). For example, by default, NCSA HDF assumes that a 100 x 200 image with three components (R, G, and B) is stored as an array of size 100 x 200 x 3, and that each element of this array is exactly one byte in size and contains an R, G, or B value. Specifically, an interlace code of 0 indicates that the bytes that describe the image are stored in the following order in the file or memory: R, G, and B values are stored, in that order, in the first three bytes of the array, corresponding to the first pixel in the first row of the image. R, G, and B values are stored in the second three bytes of the array, corresponding to the second pixel in the first row of the image. And so forth, until the RGB values for the 100 pixels of the first row of the image are stored. This process is repeated until the RGB values for each pixel in the 200 lines of the image are stored. Scan-Line Interlace Scheme The scan-line interlace scheme describes an image line-by-line. The code to specify the scan-line interlace scheme is 1. For example, an interlace scheme code of 1 for a 100 x 200 image with three components (R, G, and B) informs NCSA HDF to assume that the image is stored as an array of size 100 x 3 x 200. Specifically, an interlace code of 1 indicates that the bytes that describe the image are stored in the following order in the file or memory: 100 R values are stored consecutively in the first 100 bytes of the array for each of the pixels in the first line of the image, then 100 G values are stored in the second 100 bytes of the array for each of the pixels in the first line of the image, then 100 B values are stored in the third 100 bytes of the array for each of the pixels in the first line of the image, and so forth, until the RGB values for each of the 200 lines of pixels in the image are stored. Scan-Plane Interlace Scheme The scan-plane interlace scheme describes an image color component-by-color component. The code to specify the scan- plane interlace scheme is 2. For example, an interlace scheme code of 2 for a 100 x 200 image with three components (R, G, and B) informs NCSA HDF to assume that the image is stored as an array of size 3 x 100 x 200. Specifically, an interlace code of 2 indicates that the bytes that describe the image are stored in the following order in the file or memory: R values are stored in the first 100 x 200 bytes of the array for each of the pixels in the image; the G values for the image are stored in the second 100 x 200 bytes; and the B values in the third. Figure 2.8 illustrates how an RIS24 -- stored using the scan-plane interlace -- looks in an HDF file. Figure 2.8 Scan-Plane Interlace Compression Schemes As of this writing, image compression has not been implemented for 24-bit images in HDF. However, there are plans to implement a routine to cause 24-bit images to be stored in compressed mode. Writing 24-Bit Raster Images to a File DF24setil FORTRAN: INTEGER FUNCTION d2setil(il) INTEGER il; - interlace of image C: int DF24setil(il) int il; /* interlace of image */ Purpose: To set interlace scheme to be used on subsequent writes. Returns: 0 on success; Ð1 on failure. If DF24setil is not called, the interlace code is assumed to be 0. Interlace codes: 0 = pixel interlacing; 1 = scan-line interlacing; 2 = scan-plane interlacing. DF24addimage FORTRAN: INTEGER FUNCTION d2aimg(name, image, width, height) CHARACTER*(*) name - name of HDF file CHARACTER*(*) image - array holding image to add to file INTEGER width, height - dimensions of array image C: int DF24addimage(filename, image, width, height) char *filename; /* name of HDF file */ void *image; /* pointer to the array holding image to add to file */ int32 width, height; /* dimensions of array image */ Purpose: To write out a 24-bit image. Returns: 0 on success; Ð1 on failure. Array image is assumed to be width x height x 3 bytes. NOTE: The order in which you declare dimensions is different between C and FORTRAN. Ordering varies because FORTRAN arrays are stored in column-major order, while C arrays are stored in row-major order. (Row-major order implies that the last coordinate varies fastest). When DF24addimage writes an image to a file, it assumes row-major order. The FORTRAN declaration that causes an image to be stored in this way must have the width as its first dimension and the height as its second dimension. To take this into account as you build your image in your FORTRAN program, you need to build the image "on its side." Hence, in FORTRAN programs, array image should be declared as: image(3, width, height) for interlace=0 image(width, 3, height) for interlace=1 image(width, height, 3) for interlace=2 In C programs, array image should be declared as: image[height][width][3] for interlace=0 image[height][3][width] for interlace=1 image[3][height][width] for interlace=2 DF24putimage FORTRAN: INTEGER FUNCTION d2pimg(name, image, width, height) CHARACTER*(*) name - name of HDF file CHARACTER*(*) image - array holding image to add to file INTEGER width, height - dimensions of array image C: int DF24putimage(filename, image, width, height) char *filename; /* name of HDF file */ void *image; /* pointer to array for image to add to file */ int32 width, height; /* dimensions of array image */ Purpose: To write out a 24-bit image as the first image in the file, overwriting any information that was previously in the file.. Returns: 0 on success; Ð1 on failure. Array image is assumed to be width x height x 3 bytes. See DF24addimage for how to declare array image in FORTRAN and C programs. NOTE: DF24putimage overwrites any information that exists in the HDF file. To write an image to a file by appending it, rather than overwriting it, use DF24addimage (see above) . Example: Writing a RIS24 with the Default Pixel Interlace The C code in Figure 2.9 demonstrates how the default pixel interlace is used when writing the 400 x 600 image stored in array picture in a file in RIS24 format. Figure 2.9 Storing an RIS24 Using Pixel Interlace C: char picture[400][600][3]; int ret; ret = DF24addimage("herfile.hdf",picture,600,400); if (ret != 0) printf("Error writing image to myfile.hdf."); ... Example: Writing Several 24-bit Images Figure 2.10 shows a series of C calls in which four 800 x 1200 images are written to the same file. The first two calls use the default interlace scheme; the second two calls use scan-line interlace. Figure 2.10 Storing Multiple RIS24s in a Single File C: int DF24addimage; char pic1[800][1200][3], pic2[800][1200][3]; char pic3[800][3][1200], pic4[800][3][1200]; DF24addimage("myfile",pic1,1200,800); DF24addimage("myfile",pic2,1200,800); DF24setil(1); DF24addimage("myfile",pic3,1200,800); DF24addimage("myfile",pic4,1200,800); ... Reading 24-Bit Raster Images from a File The two routines, DF24getdims and DF24getimage, are sufficient to read raster images from a file. If enough is known about the images and interlacing, only the latter routine is needed. DF24getdims FORTRAN: INTEGER FUNCTION d2gdims(name, width, height, il) CHARACTER*(*) name - name of HDF file INTEGER width, height - for returning dimensions INTEGER il - for returning interlace of image in file C: int DF24getdims(filename, pwidth, pheight, pil) char *filename; /* name of HDF file */ int32 *pwidth, *pheight; /* for returning dimensions */ int *pil; /* for returning interlace of image in file */ Purpose: To get dimensions and interlace storage scheme of next image RIS. Returns: 0 on success; Ð1 on failure. If the file is being opened for the first time, DF24getdims returns information about the first image in the file. If an image has already been read, DF24getdims finds the next image. Thus, images are read in the same order in which they were written to the file. If you know the dimensions of the image beforehand, there is no need to call DF24getdims. Simply allocate arrays with the proper dimensions for the image and let DF24getimage read in the images. If, however, you do not know the values of width and height, you must call DF24getdims to get them and then use them to determine the right amount of space needed for the array image. Successive additional calls to DF24getdims and DF24getimage, respectively, retrieve all of the images in the file in the sequence in which they were written. Interlace codes: 0 = pixel interlacing; 1 = scan-line interlacing; 2 = scan-plane interlacing. DF24getimage FORTRAN: INTEGER FUNCTION d2gimg(name, image, width, height) CHARACTER*(*) name - name of HDF file CHARACTER*(*) image - pointer to space to return image INTEGER width, height - dimensions of space to return image C: int DF24getimage(filename, image, width, height) char *filename; /* name of HDF file */ void *image; /* pointer to space to return image */ int32 width, height; /* dimensions of space to return image */ Purpose: To get image from next 24-bit RIS. Returns: 0 on success; Ð1 on failure. If DFR24getdims has not been called, DFR24getimage finds the next image in the same way that DFR24getdims does. The amount of space allocated for the image should be width x height x 3 bytes. To specify that the next call to DF24getimage should read the raster image from the RIS24 using a particular interlace, rather than the interlace used to store the image in the file, make a call to DF24reqil (see below). DF24readref FORTRAN: INTEGER FUNCTION d2rref(name, ref) character*(*) name - name of file containing image integer ref - reference number for next d2gimg C: int DF24readref(filename, ref) char *filename; /* file containing image */ uint16 ref; /* reference number for next DF24getimage */ Purpose: To specify the reference number of the image to be read when DF24getimage is next called. Returns: 0 on success; -1 on failure. You will most likely use this routine in conjunction with DFANgetlablist1, which returns a list of labels for a given tag together with their reference numbers. It provides, in a sense, a random access to images. NOTE: There is no guarantee that reference numbers appear in sequence in an HDF file; therefore, it is not safe to assume that a reference number is the sequence number for an image. DF24reqil FORTRAN: INTEGER FUNCTION d2reqil(il) INTEGER il - interlace to get next image with C: int DF24reqil(il) int il; /* interlace to get next image with */ Purpose: To cause next DF24getimage to store image in memory with the specified interlace. Returns: 0 on success; Ð1 on failure. Regardless of what interlace scheme is used to store the image, DF24reqil causes the image to be loaded into memory and be interlaced according to the specification of il. NOTE: Since a call to DF24reqil may require a substantial reordering of the data, I/O performance could be adversely affected; e.g. it could result in much slower I/O performance than would be achieved if no change in interlace were requested. Interlace codes: 0 = pixel interlacing; 1 = scan-line interlacing; 2 = scan-plane interlacing. DF24restart FORTRAN: INTEGER FUNCTION d2first() C: int DF24restart() Purpose: To cause the next get to read from the first RIS24 in the file, rather than the RIS24 following the one that was most recently read. Returns: 0 on success; -1 on failure DF24lastref FORTRAN: (not yet implemented in FORTRAN) C: uint16 DF24lastref() Purpose: To get last reference number written or read for an RIS24. Returns: Reference number on success; -1 on failure. This routine is primarily used for annotations. See Chapter 5, "Annotating Data Objects and Files," for examples. Examples Involving 24-bit Raster Image Sets Example: Reading in a 24-bit Image Figure 2.11 shows a C call that reads in an image when the dimensions and interlace are already known and scan-plane interface is being used.. Figure 2.11 Reading an RIS24: Dimensions and Interlace Known C: char image[3][256][512]; DF24getimage("myfile.hdf",image,512,256); ... Example: Read an Image, Dimensions and Interlace Not Known Figure 2.12 illustrates a set of C calls that read in an image, where the dimensions of the image and interlace scheme are not known ahead of time. The data is stored in the array image as if the array were three planes of size width x height. Since no explicit declaration is given for image, it is the responsibility of the program to compute offsets in the array that correspond to particular elements. Figure 2.12 Reading an RIS24: Dimensions and Interlace Not Known C: #include "hdf.h" ... int32 width, height; intn il; char *image; /* pointer to space to return image */ DF24getdims("myfile.hdf",&width,&height,&il); DF24reqil(2); image = (char *) malloc(3*width*height); DF24getimage("myfile.hdf",image,width,height); ... Sample Programs A C Program to Convert a Raw Palette and Raw Raster Image to HDF RIS8 Format The example in Figure 2.13 shows a complete program for processing RIS8 data. Several features of HDF image storage are illustrated here. The program does the following, in order: * Reads into image1 a 256 x 512 8-bit raster image. * Reads into each of red, green, and blue 256 values representing the palette from a (non-HDF) file called ps.pal * Writes the palette and image as a run-length encoded raster image set to a file called testrig1.hdf * Reads the palette and image back in. * Compares the contents of image2 to the contents of image1 to determine whether they are identical, as they should be Figure 2.13 C Program Dealing with Raster Image Sets main { char image1[131072], /* raw image to be read in then */ /* put into an RIS8 in testrig.df */ image2[131072], /* HDF image to be read from testrig.df */ palette[768], reds[256], greens[256], /* colors to load into palette */ blues[256], *p; /* pointer to palette */ int j, width, height, ispal; /* to tell if there is a palette */ FILE *fp; fp = fopen("denaa031","r"); /* read in raw 256x512 image */ fread(image1, 256, 512, fp); fclose(fp); fp = fopen("ps.pal","r"); /* read RGB values from palette file */ fread(reds,1,256,fp); fread(greens,1,256,fp); fread(blues,1,256,fp); fclose(fp); p = palette; /* reorganize palette so that */ for (j=0; j<256; j++) { /* RGB values are interleaved */ *p++ = reds[j]; *p++ = greens[j]; *p++ = blues[j]; } /* write out image with palette */ DFR8setpalette(palette); DFR8putimage("testrig1.df",image1,256,512,DFTAG_RLE); /* read in image with palette */ DFR8getdims("testrig1.df",&width, &height, &ispal); DFR8getimage("testrig1.df",image2,width,height,palette); if (memcmp(image1, image2, 131072) ==0) /* compare the images */ printf("identical\n"); else printf("different\n"); } C Functions to Convert Floating- Point Data to 8-Bit Raster Data The function floattor8 shown in Figure 2.14 converts a floating- point data array into an 8-bit raster array. Once converted, this raw raster array is can be stored in RIS8 format. Figure 2.14 Converting Floating-Point Data to RIS8 C: /* * floattor8.c */ #define CHAR_MAX 255 /* * floattoR8 * Convert a data array into a raster array by dividing the * range in the data into 256 regions, numbering the regions * from zero to 255, and assigning to each position in the * raster array the number of the corresponding region. */ floattoR8(data, raster, size, max, min) float data[]; char raster[]; int size; float max, min; { int32 i; float32 step; if ((max == 0) && (min == 0)) findMaxMin(data, size, &max, &min); step = CHAR_MAX / (max - min); if (step == 0) return(-1); for(i=0;i data[i]) *min = data[i]; } } 1This routine is discussed in the chapter "Annotating Data Objects and Files." 1This routine is discussed in the chapter "Annotating Data Objects and Files."