007 1387 050

User Manual: 007-1387-050

Open the PDF directly: View PDF PDF.
Page Count: 450

Download007-1387-050
Open PDF In BrowserView PDF
ImageVision Library™
Programming Guide

Document Number 007-1387-050

CONTRIBUTORS
Written by George Eckel, Jackie Neider, and Eleanor Bassler
Illustrated by Seth Katz, Nancy Cam, Bill Pickering, and Eleanor Bassler
Edited by Nan Schweiger
Engineering contributions by Chris Walker, Nancy Cam, Venkatesh Narayanan,
Dan Baca, Jon Brandt, Don Hatch, and Casey Leedom
Photography by Jackie Neider, Jim Winget, Nancy Cam, and Judith Quenvold
Cover St. Peter’s Basilica image courtesy of ENEL SpA and InfoByte SpA. Disk
Thrower image courtesy of Xavier Berenguer, Animatica.
© 1993, 1995, 1996, Silicon Graphics, Inc.— All Rights Reserved
The contents of this document may not be copied or duplicated in any form, in whole
or in part, without the prior written permission of Silicon Graphics, Inc.
RESTRICTED RIGHTS LEGEND
Use, duplication, or disclosure of the technical data contained in this document by
the Government is subject to restrictions as set forth in subdivision (c) (1) (ii) of the
Rights in Technical Data and Computer Software clause at DFARS 52.227-7013
and/or in similar or successor clauses in the FAR, or in the DOD or NASA FAR
Supplement. Unpublished rights reserved under the Copyright Laws of the United
States. Contractor/manufacturer is Silicon Graphics, Inc., 2011 N. Shoreline Blvd.,
Mountain View, CA 94043-1389.
Silicon Graphics and IRIS are registered trademarks and IRIS-4D, IRIX, IRIS Graphics
Library, IRIS IM, ImageVision, ImageVision Library, and RealityEngine are
trademarks of Silicon Graphics, Inc. Motif is a trademark of Open Software
Foundation. UNIX is a registered trademark of UNIX System Laboratories. X
Window System is a trademark of the Massachusetts Institute of Technology.
Microsoft is a registered trademark of Microsoft Corporation. Apple and Macintosh
are registered trademarks of Apple Computer, Inc. Kodak and Kodak Photo CD are
trademarks of Eastman Kodak Company.
Red-tailed boa photograph property of Judith Quenvold.

ImageVision Library™ Programming Guide
Document Number 007-1387-050

Contents

List of Figures

xiii

List of Tables xvii
List of Examples xix
About This Guide xxi
What This Guide Contains xxi
Suggestions for Further Reading xxiii
Adding a User Interface to Your ImageVision Library Program
Style Conventions xxvi
1.

Writing an ImageVision Library Program 1
A Sample Program in C++ 2
C++ Version of the Sample Program 3
More about the Sample Program 4
The C Interface 9
Creating and Deleting C++-style Objects 9
Calling Functions 10
Including Header Files 11
A Sample Program in C 11

2.

The ImageVision Library Foundation 15
The IL Class Hierarchy 15
Foundation Classes 16
The ilLink Class 17
The ilImage Class 19

xxv

iii

Contents

Image Attributes 20
Error Codes 22
Size 22
Data Type 23
Data Ordering 24
Color Model 25
Determining Operator Data Types, Ordering, Working Types, and Definable Fields
26
Color Palette 27
Orientation 28
Fill Value 29
Minimum and Maximum Pixel Values 30
Data Compression 32
The Cache 32
Managing Cache 35
Priority 36
Page Size 38
Multi-threaded Paging Support 39
Accessing Image Data 40
Two-dimensional Functions 40
Three-dimensional Functions 46
Data Access Support Functions 47
Orientation Support 48
Geometric Mapping Support 49
The IL Execution Model 50
On-demand Processing 50
Multi-threading 53
Using Graphics Hardware for Acceleration 55
Working with Image Chains 56
Dynamically Reconfiguring a Chain 57
Propagating Image Attributes 59
Object Properties 61

iv

Contents

3.

Accessing External Image Data 65
Supported IFL Image File Formats 66
FIT 66
GIF 66
JFIF (JPEG) 67
ilTCL 67
Kodak Photo CD Image Pac 67
Kodak Photo CD Overview Pac 69
PNG 69
PPM/PGM/PBM 69
Raw 69
SGI 70
TIFF 70
YUV 71
Alias 71
SOFTIMAGE 71
Using IL to Access an Image 71
Opening an Existing File 71
Creating an Image File 73
Setting a File’s Compression 75
Querying a File Image 76
Setting and Getting Special Image Properties
Importing and Exporting Image Data 78
Images in Memory 78

77

v

Contents

vi

4.

Operating on an Image 81
Image Processing Operators Provided with IL 84
Color Conversion and Transformation 85
Arithmetic and Logical Transformations 90
Geometric Transformations 98
Spatial Domain Transformations 106
Edge Detection 117
Frequency Domain Transformations 120
Generation of Statistical Data 132
Radiometric Transformations 136
Combining Images 146
Constant-valued Images 152
Using a Null Operator 152
Defining a Region of Interest 153
Creating an ilRoiImg 154
Creating an ilSubImg 156

5.

Displaying an Image 159
Overview of the Display Facility 160
Scrolling Windows 164
A Simple Interactive Display Program 165
Sample Program Code 165
Sample Program Comments 167
Creating an ilDisplay 169
Opening an X Window and Creating an ilDisplay Object
Adding a View to the ilDisplay Object 170
Deallocating the Display 171
Choosing OpenGL or X Rendering 171

169

Contents

View and Display Basics 171
Background Color 172
Borders 172
Preventing View Operations 174
Deferring Drawing 174
The Drawing Area 175
Managing the Cache 175
Mode Flags 175
Managing Views 177
Adding Images 177
Stereo Viewing 178
Retrieving Views 179
Retrieving Images 179
Removing Views 180
Replacing Images 180
Reordering the View Stack 180
Finding a View 181
Finding an Edge 181
Operating on a Pixel 182
Locating a Point 183
Applying a Display Operator 184
Drawing Views 184
Relocating Views and Images 188
Resizing Views 193
Updating Views 195
Using setMouse() 196
A More Complicated Interactive Display Program
6.

196

Extending ImageVision Library 199
Deriving From ilImage 202
Data Access Functions 203
Color Conversion 207
Managing Image Attributes 207
Deriving From ilCacheImg 212

vii

Contents

Deriving From ilMemCacheImg 213
Implementing an Image Processing Operator 215
Deriving From ilOpImg 217
Handling Image Processing 221
Deriving From ilMonadicImg or ilPolyadicImg 228
Deriving From ilSpatialImg 234
Deriving New Classes From ilWarpImg and ilWarp 237
Deriving From ilFMonadicImg or ilFDyadicImg 238
Deriving From ilFFiltImg 241
Deriving From ilRoi 242
Using an ROI: The ilRoiIter class 243
Deriving New Classes From ilRoi 243
Deriving New Classes From ilRoiIter 243
7.

viii

Optimizing Your Application 247
Managing Memory Usage 247
Optimizing Use of Cache 247
Page Size 251
Buffer Space 253
Using Hardware Acceleration 253
Using Accelerated Operators 253
Understanding the OpenGL Imaging Pipeline 255
Composing Operators 256
Pixel Buffers and Multi-Pass Acceleration 258
Texture 259
Texture Allocation 266
Hardware-Specific Acceleration Restrictions 266
General Restrictions 266
InfiniteReality 267
Reality Engine 267
Impact/High Impact 268
Indy/Indigo2 268

Contents

Hardware Hints 268
Using IL-Recognized Hints 269
Creating Your Own Hints 271
8.

The Programming Environment 273
Compiling and Linking an IL Program 273
Programs Written in C++ 273
Programs Written in C 275
Reading the Reference Pages 275
Image Tools 277
Online Source Code 277
Environment Variables 278
Caching Configuration Issues 280
Hardware-Acceleration Configuration Issues 280
Hardware Display Configuration Issues 280
Monitoring Control Issues 281
Multi-Threading Configuration Issues 283

A.

What is New in Version 3.1 285
keepPrecision() Added to ilOpImg 285
Multiprocessing on Single CPU Machines Enabled
Additional Image Formats Supported 285
ELT Performance Enhanced 286
Choosing OpenGL or X Rendering 286
API Change for ilImgStat 286

B.

285

What is New in Version 3.0 287
Overview of Changes in 3.0 287
Understanding the New Features 288
Support for OpenGL and Hardware Acceleration
64-bit Address Space Support 289
Understanding New Classes 289

288

ix

Contents

Understanding the Changes to the Existing Features
Multi-threading Architecture Changes 292
Asynchronous Operations 292
Changes to the Display Facility 294
Error handling 295
Polynomial Coordinate Structures 296
Run-time Object-Type Query Macros 297
Changes to Existing Classes 298
Backwards Compatibility with IL 2.5 306
Automatic Class Name Conversion 308
New Derivations for Classes 313

x

292

C.

Introduction to C++ 315
Objects and Classes 315
Overloaded Functions 316
Inheritance 317
Public versus Protected versus Private 318
Passing by Reference 318
Default Values 318
Class Declaration Format 319
Linking with Libraries in Other Languages 319
Referring to Function Names 321

D.

Summary of All Classes 323

E.

Implementing Your Own Image File Format 337
Deriving and Implementing Your Image File Format Class
Opening an Existing File 338
Creating a New Image File 340
Closing a File 342
Parsing the File Name 344
Reading and Writing Formatted Data 345
Functions that Manipulate the Image Index 348
Adding Images to Image Files 349

337

Contents

Deriving an Image File Format from iflFormat 350
Deriving Subclasses 350
Virtual Function Descriptions 351
Sample Code for Virtual Function Definitions 353
Registering an Image File Format 354
Using the File Format Database 354
F.

G.

Auxiliary Classes, Functions, and Definitions 357
Auxiliary Classes 358
iflConfig 359
Using iflLut 360
Useful Functions 362
Computing the Size of Data Types 363
Minimum and Maximum Comparisons 364
Converting to Color-index Mode 364
Convenient Structures 365
Coordinate Data Structures 365
Error Codes 366
ilStatus Error Codes 366
iflStatus Error Codes 368
Enumerated Types and Constants 369
Describing Image Attributes 370
Using the Electronic Light Table 375
Understanding How ELT Works 375
DeWarping the Image 377
RotZooming the Image 377
Convolving the Image 377
Collecting Histogram Data 378
Dynamically Adjusting the Image 379
DeWarping the Image Data 379
Enabling and Disabling Operators 380
Setting Operator Values 381

xi

Contents

Understanding Accelerated Performance 381
Look-ahead Algorithms 381
Hardware Acceleration 382
Image Size 382
Choosing a Display in ELT Applications 383
Creating an ELT Application 383
Understanding the ilELTImg API 388
H.

Results of Operators 395
Color Conversion 396
Arithmetic and Logical Transformations 397
Geometric Transformations 400
Spatial Domain Transformations 401
Edge Detection 402
Frequency Domain Transformations 404
Radiometric Transformations 405
Combining Images 407
Index

xii

409

List of Figures

Figure 1-1
Figure 1-2
Figure 2-1
Figure 2-2
Figure 2-3
Figure 2-4
Figure 2-5
Figure 2-6
Figure 2-7
Figure 2-8
Figure 2-9
Figure 2-10
Figure 2-11
Figure 2-12
Figure 2-13
Figure 2-14
Figure 2-15
Figure 4-1
Figure 4-2
Figure 4-3
Figure 4-4
Figure 4-5
Figure 4-6
Figure 4-7
Figure 4-8
Figure 4-9

An Image before Processing 6
The Image after Processing 8
The ilLink Class Inheritance 17
An IL Chain 18
Sizes of Original and Processed Images 23
Pixel Data Ordering for an RGB Image 24
Determining Color Model Inheritance for Operator Images 26
Image orientations 29
Cache Containing Portions of Three Images 33
Pages and Tiles of Image Data 34
Priority Lists in Cache 37
Parameters for getSubTile() and setSubTile() 45
Image Chain for the Sample Program 51
Image Chain Showing Demand-driven Execution Model 52
Performance Comparison of Non-threaded, Single-processor, and
Multi-processor Applications 53
Operators, Requests for Pages, and Threads 55
An Image Chain 57
ilOpImg and IL Inheritance Hierarchy 82
Color Conversion Operators Inheritance Hierarchy 85
Determining the Color Model of Multi-Input Operators 87
A Falsely Colored Image 89
Arithmetic and Logical Operators Inheritance Hierarchy 91
A Positive and Negative Image Pair 93
Adding Two Images 96
Minimum of Two Images 97
Logical AND and OR of Two Images 98

xiii

List of Figures

Figure 4-10
Figure 4-11
Figure 4-12
Figure 4-13
Figure 4-14
Figure 4-15
Figure 4-16
Figure 4-17
Figure 4-18
Figure 4-19
Figure 4-20
Figure 4-21
Figure 4-22
Figure 4-23
Figure 4-24
Figure 4-25
Figure 4-26
Figure 4-27
Figure 4-28
Figure 4-29
Figure 4-30
Figure 4-31
Figure 4-32
Figure 4-33
Figure 4-34
Figure 4-35
Figure 4-36
Figure 4-37
Figure 5-1
Figure 5-2
Figure 5-3
Figure 5-4
Figure 5-5

xiv

A Warped Image 99
Geometric Operator Inheritance Hierarchy 99
Warping an Image 104
Spatial Domain Operator Inheritance Hierarchy 106
The ilPadSrc Edge Mode 108
An Original Image 110
An Image Blurred with ilBlurImg 111
An Image Sharpened with ilSharpenImg 112
An Over-sharpened Image 112
Median Rank Filtering on an Image 114
Edge Detection Operator Inheritance Hierarchy 117
Edge Image Produced by ilRobertsImg 118
A Compass Filtered Image 120
Frequency Domain Operator Inheritance Hierarchy 121
Magnitude and Phase Fourier Operators 125
Original Image 129
Image Processed with ilFGaussFiltImg 129
The ilImgStat Inheritance 133
Radiometric Operator Inheritance Hierarchy 137
Using Scaling 139
Breakpoints along a Piecewise Continuous Function 143
Using a Lookup Table Editor to Set Breakpoints 146
ilBlendImg, ilMergeImg, and ilCombineImg Inheritance Hierarchy 146
Blended Images 148
Composition Modes for ilBlendImg 150
lRoi’s Subclasses 155
Source Image and Subimage 156
Translated Subimage 157
IL Display Classes 160
Stacked Images in an X Window 161
ilDisplay Object Creates a Display Area 162
ilView Objects Map Images to Display Regions 163
Display Area After Views Are Drawn 164

List of Figures

Figure 5-6
Figure 5-7
Figure 5-8
Figure 5-9
Figure 5-10
Figure 6-1
Figure 6-2
Figure 6-3
Figure 6-4
Figure 7-1
Figure 7-2
Figure 7-3
Figure 7-4
Figure 7-5
Figure 7-6
Figure 7-7
Figure 7-8
Figure 7-9
Figure 7-10
Figure C-1
Figure G-1
Figure H-1
Figure H-2
Figure H-3
Figure H-4
Figure H-5
Figure H-6
Figure H-7
Figure H-8
Figure H-9
Figure H-10
Figure H-11
Figure H-12

Aligning an Image to Bottom Left Corner 189
Aligning Views 189
split() with ilAbsSplit | ilRowSplit | ilColSplit 192
split() with ilRelSplit | ilRowSplit | ilColSplit 192
Using wipeSize() 195
User-Defined Classes in IL 200
ilOpImg and Its Subclasses for Deriving 216
Using qgetSubTile3D() 223
Visualizing a ROI 242
Varying Page Dimensions 249
OpenGL Image Processing Pipeline 256
IL Chain Mapped to the OGLIP Pipeline 257
Mapping onto the OGLIP in a Single Transfer 257
Running a Subsection of an IL Chain 258
Two-Pass Transfer Operations 259
Accelerating an IL Chain Using Texture 260
Data Path of the IL Chain in Figure 7-7 261
Hardware Acceleration Without Using Pixel Buffers 263
Hardware Acceleration Using Pixel Buffers 265
Sample Inheritance Hierarchy 317
ELT image processing pipeline 376
ilFalseColorImg 396
ilGrayImg 396
Original Image and Flipped Image 397
ilAddImg and ilAndImg 397
ilDivImg 397
ilExpImg and ilInvertImg 398
ilLogImg and ilMaxImg 398
ilMinImg and ilMultiplyImg 398
ilNegImg and ilOrImg 399
ilPowerImg and ilSqRootImg 399
ilSquareImg and ilSubtractImg 399
ilXorImg 400

xv

List of Figures

Figure H-13
Figure H-14
Figure H-15
Figure H-16
Figure H-17
Figure H-18
Figure H-19
Figure H-20
Figure H-21
Figure H-22
Figure H-23
Figure H-24
Figure H-25
Figure H-26
Figure H-27
Figure H-28
Figure H-29
Figure H-30

xvi

Original and ilRotZoomImg 400
ilWarpImg 401
Original, ilBlurImg and ilGBlurImg 401
ilDilateImg, ilErodeImg, and ilMaxFltImg 401
ilMedFltImg, ilMinFltImg, and ilSharpenImg 402
ilCompassImg 402
ilLaplaceImg (original and filtered image) 402
ilRobertsImg (original and filtered image) 403
ilSobelImg (original and filtered image) 403
ilFGaussFiltImg 404
ilHistEqImg (filtered image and histogram) 405
ilHistNormImg (filtered image and histogram) 405
ilHistScaleImg (filtered image and histogram) 406
ilLutImg (original, filtered image, and LUT editor) 406
ilThreshImg 406
Originals and Original Mask 407
ilBlendImg 407
ilCombineImg 408

List of Tables

Table 1-1
Table 2-1
Table 2-2
Table 2-3
Table 3-1
Table 3-2
Table 3-3
Table 4-1
Table 4-2
Table 4-3
Table 4-4
Table 4-5
Table 6-1
Table 6-2
Table 6-3
Table 6-4
Table 6-5
Table 6-6
Table 7-1
Table 8-1
Table B-1
Table B-2
Table B-3
Table B-4
Table D-1

IFL-supported Image Formats 6
Image Attribute Summary 21
Data Access Functions 40
Channel Mapping 43
Compression Algorithms Supported for ilTIFFImg Files 75
File Query Functions 76
Color Models 79
Single-input Arithmetic Operators and Their Valid Output Data Types
92
Compass Directions for the ilCompassImg Operator 119
Output of a Forward Fourier Transform (if nx and ny are even) 123
Output of a Forward Fourier Transform (if nx and ny are odd) 123
Sample Parameter Values for ilFGaussFiltImg 128
Image Attributes Needing Initialization in ilImage Subclass 202
ilImgParam Constants 209
Additional Attributes Needing Initialization in ilMemCacheImg
Derived Classes 214
Classes Derived from ilMonaDicImg and ilPolyadicImg 229
ilSpatialImg’s Subclasses 234
The Subclasses of ilFMonadicImg and ilfDyadicImg 238
ilHwHint Definitions 270
Environment Variable Definitions 278
New Names for Polynomial Structures 297
Run-time Object Inquiries 297
Class Name Conversions 308
New Class Hierarchies 313
Summary of All Classes 323

xvii

List of Tables

Table E-1
Table F-1
Table F-2
Table F-3
Table G-1

xviii

iflFormat’s Virtual Functions 351
Coordinate Data Structures 365
ilStatus Error Codes 366
iflStatus Error Codes 368
Methods in ilELTImg 388

List of Examples

Example 1-1
Example 1-2
Example 3-1
Example 5-1
Example 5-2
Example 6-1
Example 6-2
Example 6-3
Example 6-4
Example 6-5
Example 6-6
Example 6-7
Example 6-8
Example 6-9
Example 6-10
Example 6-11
Example 7-1
Example 8-1
Example 8-2
Example C-1
Example E-1
Example E-2
Example E-3
Example E-4

Sample Program (in C++) Using X Window Management 3
Sample Program (in C) Using X Window Management 11
Opening an Image File and Reading Data 72
A Simple Interactive Display Program 165
A More Complicated Interactive Display Program 197
Typical Header for a Class Derived From ilOpImg 217
Typical Constructor for a Class Derived From ilOpImg 218
The resetOp() Function of ilMonadicImg 220
A Request-Processing Implementation for a Class Derived From
ilOpImg 224
Computing the Pixelwise Sum of Two Images 226
Implementation of ilArithDoCalc() in ilPowerImg 231
Implementation of loadLut() in ilPowerImg 232
A Class Derived From ilHistLutImg to Count Pixels 233
A Class Derived From ilConvImg to Multiply and Accumulate Data
236
Constructor and Member Functions of a Class Derived From
ilFMonadicImg to Convert Coordinates 239
A Class Derived From ilFDyadicImg to Multiply Two Fourier Images
240
Using the Hint Name to Set a Hint 269
Makefile for a C++ Program 274
Makefile for a C Program 275
Class Declaration Format 319
Opening a File 339
Creating a File 341
Closing a File 343
Flushing a Buffer 344

xix

List of Examples

Example E-5
Example E-6
Example F-1
Example F-2
Example G-1

xx

Reading and Writing Data in the FIT Format 347
Defining Virtual Functions for Your Image File Format
iflConfig Constructors and Fields 359
iflLut Constructors and Member Functions 361
Coding an ELT Application 384

353

About This Guide

The ImageVision Library™ (IL) is an object-oriented, extensible toolkit designed for
developers of image-processing applications. Typical image processing programs access
existing image data, manipulate it, display it, and save the processed results. IL provides
a robust framework within which developers can easily create such programs to run on
all Silicon Graphics® workstations.
IL consists of a library written in the C++ programming language; interfaces for the C
language are also available. The object-oriented nature of C++ provides a simplified
programming model based on abstractions of what images are and how they are
manipulated. This model relieves developers of many tedious programming details and
allows them to conceptually design creative programming solutions. Also, because IL is
written in C++, developers can easily extend it, for example, to incorporate their own
image processing algorithms or to include support for their own image file formats.
Several examples of images produced using IL appear in Chapter 4, “Operating on an
Image.”

What This Guide Contains
This guide presents a task-oriented perspective of IL. The topics in this guide are
arranged to coincide with the order in which you need to refer to them while writing an
image processing program. To illustrate the use of IL, code examples are sprinkled
liberally throughout the guide. Additional sample source code is provided online; see
“Online Source Code” on page 277. Brief descriptions of the chapters in this guide follow:
•

Chapter 1, “Writing an ImageVision Library Program,” shows what a typical image
processing application that uses IL looks like. It presents an IL program that
performs the tasks common to many image processing applications. It also
summarizes the differences among the C++, C, and Fortran interfaces to IL.

•

Chapter 2, “The ImageVision Library Foundation,” explains the general architecture
and design philosophy of IL. Most of this chapter is devoted to discussion of the
principal image class (ilImage), from which virtually all IL classes derive, and the
class that implements a key part of IL’s execution model (ilCacheImg).

xxi

About This Guide

•

Chapter 3, “Accessing External Image Data,” describes how to read and write
image data from and to either a file on disk or memory.

•

Chapter 4, “Operating on an Image,” discusses the more than 70 image processing
algorithms provided with IL. It explains how to use them and what effect they have
on image data.

•

Chapter 5, “Displaying an Image,” describes how to display and manage a set of
images on the screen in an interactive program. You can allow a user of your
program to move images, perform wipes, roam around an image, and create split
views of multiple images.

•

Chapter 6, “Extending ImageVision Library,” explains how to extend the
capabilities of IL to implement your own derived classes. You might extend IL to
include support for your own file format or to incorporate your own image
processing algorithm.

•

Chapter 7, “Optimizing Your Application,” provides information on optimizing
your IL programs by reducing memory usage, taking advantage of hardware
acceleration, and making use of IL’s multi-threading facility.

•

Chapter 8, “The Programming Environment,” provides information on the
programming environment available on Silicon Graphics workstations. It mentions
special tools that may help you in writing, compiling, and debugging your IL
program.

In addition to these chapters, this guide includes several appendices as handy
summaries of useful information:

xxii

•

Appendix A, “What is New in Version 3.1,” describes the differences between
versions 3.0 and 3.1 of the ImageVision Library.

•

Appendix B, “What is New in Version 3.0,” describes the differences between versions
2.5 and 3.0 of the ImageVision Library.

•

Appendix C, “Introduction to C++,” contains a brief introduction to the principles
of C++ programming.

•

Appendix D, “Summary of All Classes,” provides a brief summary of all the classes
that make up IL.

•

Appendix E, “Implementing Your Own Image File Format,” describes how to add
and implement your own image file format.

About This Guide

•

Appendix F, “Auxiliary Classes, Functions, and Definitions,” describes IL classes
not fully discussed elsewhere in this guide. It also lists all the error codes and
enumerated types used by IL.

•

Appendix G, “Using the Electronic Light Table,” describes the ilELT Img operator
and how you use it along with ilDisplay, ilView, and ilStereoView to create an ELT
application.

•

Appendix H, “Results of Operators,” contains illustrations showing the results of
using IL’s operators to process data.

Other documentation on IL is contained in the ImageVision Library Reference Pages. These
reference pages provide concise yet thorough descriptions of each C++ class included in
IL. They are only available online in versions for C++, C, and Fortran programmers. See
“Reading the Reference Pages” on page 275 for more information on the exact content of
the reference pages.

Suggestions for Further Reading
Because IL is written in C++, it is easiest to describe its design philosophy and how to
program with it by talking about the C++ classes that compose IL. While it is not
necessary that you know how to program in C++, you can gain more from this guide if
you understand the concepts of object-oriented programming. Where possible, however,
this guide avoids focusing on topics directly related to the C++ implementation of IL. In
addition, a brief introduction to C++ is included in Appendix C. Programming examples
in Chapter 1, “Writing an ImageVision Library Program,” are given in C++, C, and
Fortran. Some books on C++ you might find helpful include:
•

Ellis, Margaret, and Bjarne Stroustrup. The Annotated C++ Reference Manual. AT&T
Bell Laboratories, 1990. The official C++ language reference manual.

•

The C++ Programmer’s Guide. A short manual that provides information about
implementing C++ programs on Silicon Graphics workstations.

•

Lippman, Stanley. C++ Primer. AT&T Bell Laboratories, 1991. An introductory-level,
tutorial-style presentation of C++.

xxiii

About This Guide

This guide assumes that you are familiar with the principles of image processing. A
good, general discussion of image processing can be found in any of several textbooks,
such as:
•

Jain, Anil K. Fundamentals of Digital Image Processing. Prentice-Hall, Inc., 1989. A
thorough presentation of the major concepts of image processing, written for
graduate students.

•

Pratt, William K. Digital Image Processing. John Wiley & Sons, 1991.

•

Gonzalez, Rafael C., and Richard E. Woods. Digital Image Processing.
Addison-Wesley, 1992.

To learn more about the RealityEngine™ architecture, read:
•

Akeley, Kurt, and Tom Jermoluk. RealityEngine Graphics™. In Proceedings of
SIGGRAPH ‘93 (August 1993), pp. 109-116.

Most sample programs in this guide include calls to the IRIS Graphics Library™ (GL), and
IL itself uses the GL to perform rendering in the frame buffer. These calls are not
explained in much detail since the GL is documented separately in these Silicon Graphics
books:
•

Graphics Library Programming Guide

•

Graphics Library Reference Pages

•

Graphics Library Programming Tools and Techniques

IL provides support for manipulating files stored in the format defined by Tag Image File
Format (TIFF), Revision 6.0, distributed by Aldus Corp. You might want to obtain the
official specification of this format directly from Aldus (411 First Avenue South; Suite 200;
Seattle, WA 98104; (206) 628-6593).
•

TIFF 6.0 Specification

IL provides support for multi-threading on single- and multi-processor machines. If you
want to know more about writing multi-threaded applications, refer to this document:
•

xxiv

Parallel Programming on Silicon Graphics Computer Systems

About This Guide

IL uses dynamic linking. To learn more about using dynamic linking with your
applications, read:
•

the dlopen, dlsym, and dlerror reference pages

•

IRIX™ Programming Guide

Adding a User Interface to Your ImageVision Library Program
IL does not impose any particular user interface (UI), so you can use any UI toolkit—such
as IRIS IM™, Silicon Graphic’s port of the industry-standard OSF Motif™—to allow the
user to control your program. To support such interactive control, IL provides many
functions for altering parameters dynamically. IL also keeps track of when parameters
have changed so that image data can be updated automatically. These user-interface
manuals are available from Silicon Graphics:
•

OSF/Motif Programmer’s Guide

•

OSF/Motif Programmer’s Reference

•

OSF/Motif Style Guide

•

IRIS IM Programming Notes

Silicon Graphics recommends that you write mixed-model programs rather than pure
GL programs. A mixed-model program is essentially an X program that uses the GL to
handle graphics; the GL is completely removed from all areas governed by the X server.
If you are creating a mixed-model X Window System™ and IL program, you might also
want to refer to these volumes in the O’Reilly X Window System Series, published by
O’Reilly & Associates, Inc., Sebastopol, California:
•

Volume One: XLIB Programming Manual, by Adrian Nye

•

Volume Four: X Toolkit Intrinsics Programming Manual, by Adrian Nye and Tim
O’Reilly

Volumes One and Four are available from Silicon Graphics as part of the IRIS®
Development Option (IDO).

xxv

About This Guide

Style Conventions
These style conventions are used in this guide:
•

Bold—Functions, data members, and data types

•

Italics—Variables, filenames, spatial dimensions, and command

•

Regular—Class names and enumerated types

Code examples are set off from the text in a fixed-space font.

xxvi

Chapter 1

1. Writing an ImageVision Library Program

To write an image processing program, you use the C++ classes in the ImageVision
Library (IL). This chapter shows several, typical image processing applications.
This chapter contains the following major sections:
•

“A Sample Program in C++” on page 2 presents a sample program written in C++
that uses the IL. The section shows the program that uses X window management.

•

“The C Interface” on page 9 explains the differences between the C++ and C
interfaces to the IL.

•

“A Sample Program in C” on page 11 presents the sample program written in C.

1

Chapter 1: Writing an ImageVision Library Program

A Sample Program in C++
The sample C++ program presented in this section reads image data from a file,
processes it, displays it, and saves the processed data in a new file. Each task the program
performs is described in more detail in subsequent chapters. This chapter gives you a
brief introduction to the capabilities of the IL and provides you with a code example that
can serve as a template for programs you write.
Image processing applications typically perform at least some of the following tasks:
Read image data
Read formatted image data from a file on disk, for example, and
decompress it if necessary.
Process the data
Manipulate the data, for example, to enhance the original image or to
produce a statistical analysis of the data.
Display the image on the screen
Allow a user to interactively view selected portions of
simultaneously-displayed images.
Save the processed data in a file
Format and possibly compress the data.
The C++ program presented in Example 1-1 demonstrates how the IL accomplishes these
tasks. (A version of this program in the C language appears later in this chapter.) In
Example 1-1, the user invokes the program from the command line and specifies a file of
image data to be processed. The program then performs the following tasks:
1.

Opens the input file of image data.

2. Constructs a sharpening operator that uses the file of image data as input.
3. Constructs a rotate operator that uses the output of the sharpening operator as
input.
4. Displays the sharpened and rotated image data on the screen.
5. Continues to display the processed image until the user quits by pressing the 
 keys or by using the window menu.
6. Copies the sharpened and rotated image to a file on disk.

2

A Sample Program in C++

The code for this program is available online so that you can easily compile and run it.
Look in:
/usr/share/src/il/guide/sampleProg.c++
Other sample code is also available online; see “Online Source Code” on page 277.

C++ Version of the Sample Program
The code in Example 1-1 shows the C++ version of the sample program.
Example 1-1
#include
#include
#include
#include
#include
#include
#include
#include

Sample Program (in C++) Using X Window Management










void
main(int argc, char **argv)
{
// Step 1: Open the file of image data.
if (argc < 2) {
printf("Usage: %s \n", argv[0]);
exit(0);
}
ilFileImg inImg(argv[1]);
// Step 2: Create IL objects for sharpening and rotating
ilSharpenImg sharperImg(&inImg, 0.5);
ilRotZoomImg rotatedImg(&sharperImg, 90.0);
// Step 3: Set up and open a window for display.

3

Chapter 1: Writing an ImageVision Library Program

iflSize size;
rotatedImg.getDimensions(size);
Display* dpy = XOpenDisplay(NULL);
ilViewer viewer(dpy, size.x, size.y);
// Step 4: Display the processed data.
viewer.addView(&rotatedImg, ilLast, ilCenter);
// Step 5: Display until the user quits.
viewer.eventLoop();
XCloseDisplay(dpy);
// Step 6: Write the processed data to a file.
iflFileConfig fc(&size);
ilFileImg tmpFile("outFile.tif", &inImg, &fc);
tmpFile.copy(&rotatedImg);
tmpFile.flush();

}

More about the Sample Program
The sample program uses the IL in a recommended way, but many good programming
habits were not followed in the interest of keeping the program short. More specifically,
this program does not do any of the following things:
•

check return arguments and write error messages as appropriate

•

strip arguments off the command line in an elegant way and check them for
appropriate values (or provide a graphical user interface)

•

provide feedback to the user, for example, to indicate that a file of processed image
data has been created

The remainder of this section walks through Example 1-1, explaining how it uses the IL.
This discussion is intended to give you a taste of the kinds of things the IL can do and
what you, as a programmer, need to do to accomplish them. Each of the following topics
is discussed extensively elsewhere in this book.

4

A Sample Program in C++

Header Files

The first few lines of code include the necessary header files from the IL. These header
files also include other IL header files, as well as header files from the Graphics Library
and the standard C library. If you use this program as a template and modify it to suit
your needs, be sure you include the header files necessary for your program. Since the IL
provides many more capabilities than you need for any particular program, you do not
need to include all of its header files. To minimize compile time and the size of your
executable, you should include only those header files actually required by your
program.
In this example,
•

the header X11/Xlib.h is included to configure an X window for OpenGL rendering

•

the header X11/keysym.h is included to handle user input

•

the header il/ilFileImg.h is included to implement the ilFileImg class

•

the header il/ilSharpenImg.h is included to implement the ilSharpenImg class

•

the header il/ilRotZoomImg.h is included to implement the ilRotZoomImg class.

•

the header il/ilViewer.h is included to manage views in an X window.

In general, when writing an IL program in C++, you will need to include an IL header
file for each IL class you use. More information about programming and compiling IL
programs is included in “Compiling and Linking an IL Program” on page 273.
Step 1: Open the File of Image Data

In step 1 of Example 1-1, an image data file specified by the user is opened by invoking
the ilFileImg constructor. This function takes one argument: the pathname of the file. In
this example, the filename is taken as an argument from the command line and the file is
opened for reading. Figure 1-1 shows an example image file.

5

Chapter 1: Writing an ImageVision Library Program

Figure 1-1

An Image before Processing

Before any image data can be read, the ilFileImg constructor determines the format of the
image file by returning a pointer to one of the supported ilFileImg types. IL recognizes
the image file formats at runtime by searching for dynamic shared objects (DSOs) that
contain the code for specific file formats. Table 1-1 shows all of the IFL -supported image
file formats and their customary suffixes.

6

Table 1-1

IFL-supported Image Formats

File Format

Customary Suffix

SGI

.rgb, .sgi, .rgba, .bw, .screen

TIFF

.tif, .tiff

JFIF

.jpg, .jpeg, .jfif

FIT

.fit

PCD

.pcd

PCDO

pcdo

GIF

.gif

PPM

.ppm, .pgm, .pbm, .pnm

PNG

.png

Raw

.raw

A Sample Program in C++

IFL is a lower-level library upon which IL is built.
You can also create your own image file formats. For more information about defining
image file formats, see Appendix E, “Implementing Your Own Image File Format.”.
Step 2: Create IL Objects for Sharpening and Rotating

Now that the source of the image data is ready, the IL objects used for processing the data
are created in step 2. For this sample program, data is first sharpened and then rotated
by using the ilSharpenImg and ilRotZoomImg classes. These two classes demonstrate
two of the many image manipulation functions included in the IL.
As shown in Example 1-1, the parameter 0.5 is passed in with a pointer to the input
image data file to create the sharpening object. This parameter, which is a
single-precision floating point number, can range in value from 0 to 1; it defines how
much the data is sharpened. The specific algorithm that ilSharpenImg uses to sharpen
image data is described in detail in its class reference page (read “Reading the Reference
Pages” on page 275 for an explanation of the difference between normal reference pages
and class reference pages). If this were an interactive program, you could allow the user
to change the sharpness factor dynamically, perhaps with a slider widget.
You can use the ilRotZoomImg class to rotate and/or zoom (magnify or minify) an
image. In Example 1-1, the sharpened image data is rotated 90 degrees, in a
counterclockwise direction, as specified by the parameter passed to ilRotZoomImg. The
ilRotZoomImg class is discussed in detail in its reference page.
The program uses the size of the rotated image to set the size of the X window opened to
display the image.
You can invoke any number of operators on a set of data. See Chapter 4, “Operating on
an Image,” for more information about how the IL allows you to operate on image data.
You can also easily add your own algorithms; “Implementing an Image Processing
Operator” on page 215, tells you how to extend the IL to include a new image processing
operator.
As an IL program executes, image data is processed only on demand, for example, when
it’s needed for displaying or writing to a file. This execution model eliminates
unnecessary processing and minimizes transfers of data in and out of memory. In
Example 1-1, data is not actually processed until step 4. The execution model is discussed
in detail in “The IL Execution Model” on page 50.

7

Chapter 1: Writing an ImageVision Library Program

Step 3: Open a Window for Display

Example 1-1 calls the X Window library function, XOpenDisplay(), to return a pointer to
the display device which, in turn, is passed to ilDisplay to open a window.
Step 4: Display the Processed Data

In step 3, an ilViewer object is created to display the processed image data. In a more
interactive image processing program, you would use an ilViewer object to manage the
dynamic display of multiple images. Also, you could rewrite the program to display the
sharpened image before it’s rotated. Example 1-1, however, simply displays the final
image by calling the addView() member function on the sharpened, rotated image.
Displaying processed images is covered in detail in Chapter 5, “Displaying an Image.”
The result of running the sample program with the image from Figure 1-1 is shown in
Figure 1-2.

Figure 1-2

The Image after Processing

In the IL’s execution model, data is processed in conveniently sized chunks, called pages.
As you execute Example 1-1, you can watch as successive pages of image data are
displayed—one rectangular part of the image after another—after the pages have been
processed.

8

The C Interface

Step 5: Display Until the User Quits

In step 5, the program uses the eventLoop() function in ilViewer to handle X events until
the user types  . You could also write your own event loop using X library
calls and pass events you do not want to handle to the event() function in ilViewer.
Step 6: Write the Processed Data to a File

Many image processing applications need to write processed image data to a file. In step
6, the iflFileConfig function, fc(), sets the size of the image in pixels. All other image
attributes are copied from inImg which is passed to the output file object’s constructor.
The ilFileImg constructor creates a file for writing data using the TIFF file format. This
version of the constructor needs to know the name of the output file, a pointer to the
original image file, and the size of the image in pixels. See “Creating an Image File” on
page 73 for more information about ilFileImg.
The ilFileImg constructor only creates a file. The ilFileImg.copy() function actually
writes the processed (sharpened and rotated) image data directly into the file. The
ilFileImg.flush() function then writes to the disk file all pages still residing in memory.

The C Interface
Since the IL was written in C++, it implements the C interface as a wrapper to C++
member functions. This wrapper has names that are similar to those of the C++ member
functions. Thus, the concepts explained in this guide apply to C as well as C++
programmers even though most of the code examples are shown in C++.

Creating and Deleting C++-style Objects
A C++ class object must be defined as something the C language recognizes to make it
usable in a C program. For example, the header file il/ilCdefs.h defines all the IL classes as
being of data type struct. To “create” such a struct in your program, call the appropriate
function, which is usually of the form ClassNameCreate(). The call to create an ilDisplay
struct, for example, is ilDisplayCreate().

9

Chapter 1: Writing an ImageVision Library Program

In C, use these statements:
ilDisplay* disp;
disp = ilDisplayCreateWindow(dpy, size.x, size.y,
ilVisDoubleBuffer,0,0, ilDefault, ExposureMask |
KeyPressMask | StructureNotifyMask);

You can see in this example some other differences between the C and C++ calls. In C++,
you can have variables created automatically for you, or you can allocate them
dynamically yourself. The C variable disp must be declared as a pointer to type ilDisplay.
Since disp appears as just a struct to C, you need to call a destructor directly when you
need to delete it. The destructor naming scheme is similar to the creator scheme. In order
to delete the display you created with the calls above, use ilViewerDelete().
In C, use this statement:
ilViewerDelete(viewer);

In C++, use this statement:
delete ilViewer; // not needed unless created with new

Calling Functions
Once you have accomplished the C equivalent of creating an object, you can manipulate
it with the C version of the functions associated with that object. The C function name
generally includes the C++ class name, and the functions themselves take a pointer to the
“object” as an additional argument.
In C, use this statement:
status = ilDisplayAddView(disp, rotatedImg, 0, ilCenter);

In C++, use this statement:
status = disp.addView(rotatedImg);

As you can see, the C++ function addView(), which is a member function of the ilDisplay
class, becomes ilDisplayAddView(). Most functions follow this form: the name of the
base class is used as the prefix for the functions. C++ functions from the ilImage base
class (or from ilImage’s parent class, ilLink) add “il,” not “ilImage.” ilCacheImg’s flush()
function does this as well; it becomes ilFlush(), not ilCacheImgFlush().

10

A Sample Program in C

Note: The C version of the man pages list the C names for each method.

The C++ versions of the IL functions fill in default values for some arguments. If you
omit those arguments, C++ simply calls the function with the defaults. C, however, does
not fill in defaults for you. You must supply values for each argument. The C++ sample
program takes advantage of this feature when creating a new ilSharpenImg object.
In C, use this statement:
sharperImg = ilSharpenImgCreate(theImg, 0.5, 1.5, ilPadSrc);

In C++, use this statement:
ilSharpenImg sharperImg(theImg);

0.5, 1.5, and ilPadSrc are the default values for the sharpness factor, radius, and edge
mode arguments, respectively. In C, you must pass them explicitly.

Including Header Files
To use the IL in your C programs, you need to include only il/ilCdefs.h. This file includes
information about all the IL classes and functions.

A Sample Program in C
Example 1-2 shows the equivalent of Example 1-1 written in C. Example 1-2 opens a file
image, sharpens and rotates it, sets up the window configuration, opens an X window,
and displays the processed image.
Example 1-2
#include
#include
#include
#include
#include

Sample Program (in C) Using X Window Management







void
main(int argc, char **argv)
{
ilFileImg *inImg, *tmpFile;

11

Chapter 1: Writing an ImageVision Library Program

iflFileConfig *fc;
ilSharpenImg *sharperImg;
ilRotZoomImg *rotatedImg;
ilViewer *viewer;
iflSize size;
Display *dpy;
XEvent event;
int ever;
if (argc < 2) {
printf("Usage: %s \n", argv[0]);
exit(0);
}
// Step 1: Open the file of the image data.
inImg = ilFileImgOpen(argv[1], O_RDONLY, NULL);
// Step 2: Create IL objects for sharpening and rotating.
sharperImg = ilSharpenImgCreate(inImg, 0.5, 1.5, ilPadSrc);
rotatedImg = ilRotZoomImgCreate(sharperImg,90.0, 1, 1, ilBiLinear);
// Step 3: Set up and open a window for display.
dpy = XOpenDisplay(NULL);
ilGetSize(rotatedImg, &size);
disp = ilViewerCreateWindow(dpy, size.x, size.y, ilVisDoubleBuffer, 0,
0, ilDefault, ExposureMask | KeyPressMask |
StructureNotifyMask);
// Step 4: Display the processed data.
ilDisplayAddViewTop(viewer, rotatedImg, ilCenter);
ilDisplayRedraw(viewer, ilDefault);
// Step 5: Display until the user quits.
ilViewer.eventLoop(viewer);
XCloseDisplay(dpy);

12

A Sample Program in C

// step 6: Write the processed data to a file
fc = iflFileConfigCreate(&size, 0, 0, 0, 0, 0, NULL);
tmpFile = ilFileImgCreate("outFile.tif", inImg, fc, NULL);
ilCopy(tmpFile, rotatedImg);
ilImageDelete(tmpFile);
}

Example 1-2 shows several examples of function name changes, for example, the C++
call rotatedImg.getSize() becomes ilGetSize() in C.

13

Chapter 2

2. The ImageVision Library Foundation

This chapter explains the general architecture and design philosophy of the ImageVision
Library (IL). All subsequent chapters assume knowledge of the basic concepts presented
in this chapter. This chapter contains the following major sections:
•

“The IL Class Hierarchy” on page 15 gives a brief overview of the main classes that
compose the IL.

•

“Foundation Classes” on page 16 introduces the IL foundation classes, particularly
ilLink and ilImage, from which most IL classes derive.

•

“Image Attributes” on page 20 discusses in detail the attributes used to describe an
image and the functions available for retrieving and setting these attributes.

•

“The Cache” on page 32 describes the role of the cache in holding raw and
processed image data.

•

“Accessing Image Data” on page 40 discusses the general capabilities for reading
and writing image data that are common to all image classes.

•

“The IL Execution Model” on page 50 discusses the IL’s demand-driven model that
optimizes memory usage and performance as image data is processed.

•

“Working with Image Chains” on page 56 shows how you can manipulate image
chains in a dynamic environment.

•

“Object Properties” on page 61 describes how you can assign property values to
objects and retrieve these values.

The IL Class Hierarchy
The architecture and functionality of the IL is contained in a hierarchy of C++ classes.
Most of this chapter is devoted to a discussion of the principal image class (ilImage), from
which most IL classes derive, and the ilMemCacheImg class, which implements image
data caching. However, a brief look first at the IL base classes provides a perspective for
better understanding the role of the ilImage and ilMemCacheImg classes.

15

Chapter 2: The ImageVision Library Foundation

The base classes can be divided into four functional groupings:
ilLink

The ilLink class defines the linking of image operators in succession and
the images associated with these operators. See “The ilLink Class” on
page 17 for more information about the ilLink class.

Multi-threading
The IL contains several base classes that implement multi-threading in
IL. “Multi-threading” on page 53 describes how multi-threading works
in the IL. “Effect of Multi-threading on Cache” on page 249 tells you how
use multi-threading with the cache.
ilDisplay

The ilDisplay class allows you to create and manage one or more
processed images in a graphics window. Read Chapter 5, “Displaying
an Image.” to learn more about this class.

Miscellaneous Some base classes, like iflLut, iflPixel, and iflSize, provide a variety of
auxiliary functions to support the function of the IL. For example, ilPage
defines a page of image data and iflLut defines a color palette lookup
table (LUT) used to interpret the data in some images. “Auxiliary
Classes” on page 358 contains more detail about many of these
miscellaneous base classes.
All the IL classes are briefly summarized in “Summary of All Classes” on page 323.

Foundation Classes
Figure 2-1 shows the portion of the IL class hierarchy that derives from ilLink. These
classes provide much of the functionality and flexibility of the ImageVision Library.

16

Foundation Classes

ilCacheImg

ilMemCacheImg

ilOpImg
ilMemoryImg
ilImage
ilConstImg
ilRoiImg
iflListItem

Operator
classes
ilFITImg

ilFileImg
ilSGIImg
ilPCDImg

ilLink
ilTIFFImg
ilImgStat
ilRoi

ilGIFImg
ilPCDOImg

ilView
ilSwitchImg

Figure 2-1

The ilLink Class Inheritance

The foundation classes shown in shaded boxes in Figure 2-1 are abstract classes and
cannot be used directly. Understanding the capabilities these classes provide is key to
understanding how the IL works and how to use it. Also, if you extend the IL to meet
your specific image processing needs, you will derive your own classes from these
abstract classes.

The ilLink Class
The IL allows you to access, manipulate, store, and display images. You can perform a
series of operations on one or more images by creating a chain of operators and passing
the image or images down this chain. An operator is a class derived from ilOpImg (the
base class for all IL operators) that applies its image processing algorithm to an image.
The image output from each operator becomes the input to the next operator in the chain.

17

Chapter 2: The ImageVision Library Foundation

An element in a chain of operators can be:
•

an image file, for example, an ilFileImg object

•

a processing operation, which generates an image from one or more input elements,
for example, an ilAddImg object

•

an object containing statistical information about an image, for example, an
ilImgStat object

•

a region of interest (ROI), used to restrict the scope of an operator to a sub-portion
of its input elements, for example, an ilRectRoi object

•

a subsection element, which selects a portion of its input(s) to be produced as an
output, for example, an ilSubImg or ilSwitchImg object

The result of a chain of operations is either a display of the processed image or a file on
disk containing the processed image. Figure 2-2 illustrates this concept by showing a
generalized image processing chain whose elements are raw and processed images.

IL chain
disk

raw
image

operator1

processed
image

operator2

processed
image
monitor

disk

Figure 2-2

An IL Chain

The ilLink class implements the chaining model by defining the mechanism for linking
the image objects together. This model defines the concept of parent (input) and child
(output) images.
The ilLink class also provides functions that allow you to manipulate image attributes by
providing functions that keep track of whether an attribute is allowed to change or has
been altered. For more information about chaining operators, see “The IL Execution
Model” on page 50.

18

Foundation Classes

The ilImage Class
The ilImage class is the root for the majority of the IL’s image class hierarchy. It provides
the IL’s abstract concept of what images are and how they are manipulated. The IL
defines an image as a four-dimensional array of pixels, x, y, z, and c. An image has certain
attributes, such as the size (in pixels) of the image, the data type of the pixel elements (for
example, float or int), and the color model that should be used to interpret the data (for
example, RGB or CMYK).
The ilImage class provides two main categories of functions to support this abstraction
of an image:
•

image attribute functions, for querying an image about its attributes and setting
these attributes (Programmers can explicitly set some attributes, even though many
attributes are determined at the time the image is instantiated.)

•

data access functions, for reading, writing, and copying image data

All classes that derive from ilImage (see Figure 2-1) inherit these general capabilities for
querying and setting attributes and accessing data. Thus, the IL allows you to
manipulate all images in the same way, regardless of the actual source or destination of
the data. The same mechanism is used for data that is associated with any type of image,
for example:
•

an image stored in memory (ilMemoryImg)

•

an image that is displayed on the screen and that resides in the framebuffer
(ilFrameBufferImg)

•

an image operator, which applies an image processing algorithm to its data
(ilOpImg)

•

an image that resides in a file on disk and is buffered in memory (ilFileImg)

Classes derived from ilImage implement their own versions of the data access functions
as necessary to add specificity. For example, ilMemCacheImg defines versions of the data
access functions that read data from or write data to a partial copy of the image buffered
in main memory. Similarly, ilTIFFImg adds capabilities specifically for reading and
writing TIFF file headers and data. The ilSharpenImg class incorporates a sharpening
algorithm into its access functions.

19

Chapter 2: The ImageVision Library Foundation

Image Attributes
In the IL, an image has many descriptive attributes. These include:
•

image size

•

data type of image pixels

•

data ordering of channels in an image

•

color model

•

color palette

•

image type

•

orientation

•

fill value

•

minimum and maximum pixel values

•

data compression

•

page border

•

image format

Many of these attributes are assigned default values when an image is created. Some of
them are changed subsequently, usually as a result of applying—or preparing to apply—
an image operator. Some can be changed explicitly by the programmer. Each class that
derives from ilImage chooses which attributes it allows to be explicitly modified. (For
more information about how this mechanism works, see “Propagating Image Attributes”
on page 59 and “Managing Image Attributes” on page 207.)
This section describes the image attributes and the functions available for retrieving and
setting them. These functions are defined by the ilImage and ilLink classes and therefore
can be used on any type of image.

20

Image Attributes

Table 2-1 provides a summary of the image attribute functions. All of these functions are
described later in this section except for the image format (described in “Querying a File
Image” on page 76) and the page border (described in “Page Borders” on page 56).
Table 2-1

Image Attribute Summary

Image Attribute

Retrieving Attributes

Changing Attributes

Size

getSize()
getXsize()
getYsize()
getZsize()
getCsize()

setSize()
setCSize)
Apply an operator that affects the size.

Data type

getDataType()
isSigned()

setDataType()

Data ordering

getOrder()

setOrder()

Color model

getColorModel()

setColorModel()

Color palette

getColorMap()

setColorMap()

Orientation

getOrientation()

setOrientation()

Fill value

getFill()

setFill()

Min and max pixel
values

getMinPixel()
getMaxPixel()

setMinPixel()
setMaxPixel()

getMinValue()
getMaxValue()

setMinValue()
setMaxValue()

getScaleMin()

setScaleMinMax()

getScaleMax()

initScaleMinMax()

Min and max scale
values

setScaleType()
Data compression

getCompression()

setCompression()

Page border

getPageBorder()

setPageBorder()

Image format

getImageFormat()

Use the imgCopy utility to convert from
one IL-supported format to another

In addition to the functions shown above, which allow you to set image attributes
individually, you might decide to use the IL’s ilConfig class, which allows you to specify
several image attributes at once. An ilConfig object contains several elements that

21

Chapter 2: The ImageVision Library Foundation

describe pixel data: the data type, pixel ordering, number of data channels, ordering of
data channels, channel offset, orientation, and zoom factors. This class is defined in the
header file il/ilConfig.h and described in more detail in “iflConfig” on page 359 as well as
in its reference page.

Error Codes
As you read the following sections, you will note that many of the functions described
return a value of data type ilStatus. This enumerated type, which is defined in the header
file il/ilError.h, contains the error codes used by the IL to indicate that an unexpected
result occurred. If no unexpected result occurred, an image’s status is ilOKAY. The error
codes and their meanings are listed in “Error Codes” on page 366.
At any point, you can query an ilImage about its current status by using getStatus(), a
function defined in ilLink that takes no arguments and returns a value of type ilStatus.
You can also set an image’s status to ilOKAY by using clearStatus() (a function defined
in and inherited from ilLink).

Size
One key attribute of an image’s its size, which is determined initially when an image is
created. In Example 1-1 in Chapter 1, “Writing an ImageVision Library Program,” the
size of the image data is determined when the ilFileImgOpen() function is called. An
image’s size can be described with an iflSize data structure, which consists of four
integers that correspond to the image’s size in the x, y, and z dimensions and the number
of data channels, c, per pixel.
The x and y dimensions specify the width and height of the image as measured in pixels.
The z dimension, or “depth,” may refer to the number of xy planes of image data. The xy
planes are usually related in some way; for example, they might be a time-series of a
single animation scene or a set of CAT scan images. (CAT stands for computerized axial
tomography, a medical imaging technique used to create three-dimensional images.)
Different image representations require different numbers of data channels to describe
each pixel of data. An RGB (red, green, blue) image, for example, requires three channels,
one for each of the three colors.

22

Image Attributes

The ilImage class defines functions for retrieving the entire iflSize structure for an image
at once and functions for returning each of the elements separately:
ilImage myImg;
iflSize imgSize;
int imgXSize, imgYSize, imgZSize, imgChans;
myImg.getSize(imgSize);
imgXSize = myImg.getXsize();
imgYSize = myImg.getYsize();
imgZSize = myImg.getZsize();
imgChans = myImg.getCsize();

You can change an image’s size by applying an image operator that affects its size or by
setting its size explicitly (if you are allowed to set it). For example, in most cases, the
ilRotZoomImg operator produces a processed image with a size that differs from that of
the original image, as shown in Figure 2-3:
New Size

Original
Image

Figure 2-3

Rotated Image

New Size

Zoomed Image

Sizes of Original and Processed Images

You can set an image’s size explicitly by using setSize(), which takes a reference to the
desired iflSize structure as an argument. A separate function, setCsize(), allows you to
restrict the number of channels associated with an image.

Data Type
An image’s pixel components must all be of the same data type. The IL defines an
enumerated set of data types (iflDataType) and a function, getDataType(), to return the
data type of an image’s pixels:
iflDataType imgType;
imgType = myImg.getDataType();

The iflDataType returned can be one of the following: iflBit, iflChar, iflUChar (an
unsigned char), iflShort, iflUShort, iflLong, iflULong, iflFloat, or iflDouble. (These types

23

Chapter 2: The ImageVision Library Foundation

are defined in the il/iflDataTypes.h header file and listed in “Describing Image Attributes”
on page 370.)
Use isSigned() to query an ilImage about whether its data type is signed:
int sign = myImg.isSigned();

As shown, this function takes no arguments and returns TRUE (nonzero) if the image’s
data type is signed and FALSE (zero) otherwise.
Operators accept input images of any data type. Internally, however, operators may use
a different data type than the input data type to process the image. In this case, the data
is converted as needed to perform the computation. If you know what data type you
need at the end of the computation, you can use the setDataType() function to force the
data type.

Data Ordering
The channels composing an image’s pixel data can be ordered in three ways:
iflInterleaved, iflSequential, or iflSeparate. These are the three possible return values of
the enumerated type, iflOrder. To return the data ordering, use its member function,
getOrder(), as follows:
iflOrder imgOrder;
imgOrder = myImg.getOrder();

The meanings of the three orders are illustrated in Figure 2-4.

RGB
RGB
RGB
iflInterleaved

Figure 2-4

24

RRR
GGG
BBB

RRR
RRR
RRR

iflSequential

GGG
GGG
GGG

BBB
BBB
BBB

iflSeparate

Pixel Data Ordering for an RGB Image

Interleaved

In interleaved ordering, all pixel components are clustered together. For
an interleaved RGB image, data is stored as: RGBRGBRGB....

Sequential

With sequential ordering, each component is stored as a separate line. In
the example, three lines of data (one each for red, green, and blue data)
are needed to describe one line of pixels.

Image Attributes

Separate

An image using separate ordering stores each component in a separate
page. (See “The Cache” on page 32 for more information about pages.)

Thus, the order defines that dimensions that vary most rapidly relative to the others in a
chunk of data. For example, in the interleaved case, the channel dimension varies most
rapidly, and the z dimension varies least rapidly. Here is how the dimensions vary for
each of the orders, listed from most to least rapidly: iflInterleaved (c,x,y,z), iflSequential
(x,c,y,z), iflSeparate (x,y,z,c).
In the rare cases where you need to set an image’s order, use the setOrder() function.
Some classes derived from ilImage, such as ilFileImg, do not let you change an image’s
order.

Color Model
An image’s color model determines the meaning of the data channels from which a pixel
is constructed. The IL defines an iflColorModel enumerated type (in the header file
il/iflDataTypes.h) that can refer to the following color models:
iflRGB

red, green, blue

iflRGBA

red, green, blue, alpha

iflRGBPalette

color index mapped to an RGB lookup table

iflHSV

hue, saturation, value

iflCMY

cyan, magenta, yellow

iflCMYK

cyan, magenta, yellow, black

iflMinWhite

grayscale, with the minimum value interpreted as white

iflMinBlack

grayscale, with the minimum value interpreted as black

iflBGR

variation of RGB, for images generated by Silicon Graphics

iflABGR

variation of RGBA, for images generated by IRIS GL

iflMultiSpectral
generally more than three channels; requires a special interpretation
iflYCC

a luminance/chrominance data metric based on video primaries

The getColorModel() function allows you to query an image about its color model. If
necessary, you can change the data interpretation by using the setColorModel() function.

25

Chapter 2: The ImageVision Library Foundation

Determining the Color Model

If an application or derived class does not use the setColorModel() function to explicitly
set the color model of an ilOpImg object, the color model defaults to the lowest common
ancestor of the input images as shown in Figure 2-5:

iflMultiSpectral

iflRGBA

iflABGR
iflLuminanceAlpha
iflRGB

iflYCC

iflLuminence
iflBGR

iflHSV
iflNegative
iflRGBPalette

iflCMYK

iflCMY

Figure 2-5

Determining Color Model Inheritance for Operator Images

Determining Operator Data Types, Ordering, Working Types, and
Definable Fields
All classes derived from ilOpImg have specified output data types, data ordering,
working data types, and fields that can be set on an object. You can identify them by
finding the following functions in each class: setValidType(), setValidOrder(),

26

Image Attributes

setWorkingType(), and setAllowed(), respectively. For example, the ilWarpImg operator
uses an iflUChar as the output data type, can use any output ordering, uses iflFloat as the
working type, and can have any of its fields set.
You can set the data type or data order explicitly to a valid type or ordering by calling the
ilImage member function setValidType() or setValidOrder(), respectively. If the data
type or order is not set explicitly in this manner, they default to the “smallest” of the valid
types or ordering that is at least as “great” as each input type or order. Here “small” and
“great” refer to the numeric values of the types and ordering, as defined in
il/iflDataTypes.h.
An ilOpImg object has a “working type”, which is the data type used for calculations.
The working type is often the same as the output data type. When this is not the case, the
setWorkingType() function is used to define the working types.
The setAllowed() function specifies which fields can be set on an object that is an
instance of a class derived from ilOpImg.

Color Palette
Some images include a color palette that is used to interpret their data. A color palette is
also referred to as a lookup table or LUT. The most common use of such a table is to store
color map values. The iflLut class, defined in the header file ifl/iflLut.h and described in
“Using iflLut” on page 360, is provided for such purposes. To set an image’s LUT, use
setColorMap():
ilStatus setColorMap(const iflLut& lut);

The table pointed to by lut is established as the image’s look-up table. This function
copies the specified iflLut but not its data. The getColorMap() function returns by
reference an image’s LUT:
void getColorMap(iflLut& lut);

Two other functions—iflSGIColormap() and ilSGIFileLut()—create look-up tables for
use in managing color map data. They are described in “Using iflLut” on page 360 and
in their own reference pages.

27

Chapter 2: The ImageVision Library Foundation

Orientation
Different file formats arrange their data in different ways. By default, a TIFF file image
considers its origin to be the upper left corner; if you scan through the data, you should
read from left to right, working your way down the image. An SGI RGB image considers
its origin to be the lower left corner; to read through its data, again read from left to right,
but work your way up the image.
The IL defines an iflOrientation data type to represent the possible orientations of image
data. To query an image about the orientation of its data, use getOrientation(), which
returns one of the eight values listed below. (You can set an image’s orientation with the
setOrientation() function.) These four orientations use the traditional orientation of the
x and y dimensions (the x dimension runs horizontally, and the y dimension runs
vertically):
iflUpperLeftOrigin

The origin is in the upper, left corner and you read data from
left to right, working your way down the image

iflLowerLeftOrigin

The origin is in the upper, left corner and you read
data from left to right, working your way up the
image

iflUpperRightOrigin The origin is in the upper right corner, and you read data
from right to left, working your way down the image

iflLowerRightOrigin The origin is in the lower right corner, and you read
data from right to left, working your way up the
image
The following four orientations have the x and y dimensions transposed so that the x
dimension runs vertically, and the y dimension runs horizontally.
iflLeftUpperOrigin

The origin is in the upper left corner, and you read
from top to bottom, working your way across the
image to the right.

iflLeftLowerOrigin

The origin is in the lower left corner, and you read
from the bottom to the top, working your way across
the image to the right.

iflRightUpperOrigin The origin is in the upper right corner, and you read
data from top to bottom, working your way across the
image to the left.

28

Image Attributes

iflRightLowerOrigin The origin is in the lower right corner, and you read
data from bottom to top, working your way across the
image to the left.
Figure 2-6 illustrates the difference between iflUpperLeftOrigin and iflLeftUpperOrigin
orientation of image data.
Origin

Origin

.
.
.

Figure 2-6

...

iflUpperLeftOrigin

iflLeftUpperOrigin

Image orientations

Fill Value
When a function tries to access pixels that are beyond an image’s edge, those pixels are
set to the image’s fill value. By default, an image’s fill value is 0, but you can set a
different fill value with the setFill() function:
static float fillData[3] = {127.0, 127.0, 127.0};
myImg.setFill( iflPixel(iflFloat, 3, fillData) );

As shown, setFill() takes a reference to an iflPixel as an argument. An iflPixel defines the
pixel is data type (in this case, iflFloat), the number of data channels (3), and the pixel
data itself (fillData[]). (In this example, the iflPixel value is passed in-line so that the
compiler automatically constructs and deletes the object.) The image makes its own copy
of the pixel data.
Use getFill() to query an image about its fill value:
iflPixel theFillValue;
myImg.getFill(theFillValue);

29

Chapter 2: The ImageVision Library Foundation

Creating Fill Values

You use the allocFillData() and freeFillData() functions in the ilImage class to create and
free fill values in the native image format from an RGB triplet. The functions are defined
as follows:
void* allocFillData(float red, float green, float blue);
void freeFillData(void* data);

Minimum and Maximum Pixel Values
By default, no restrictions are placed on the range of allowable pixel values. However,
when an image is displayed—for example, using the ABGR color model—its pixel values
may need to be converted to the range that is meaningful for the framebuffers, which is
0 to 255. If you explicitly set an image’s minimum and maximum allowable pixel values,
they are used to color-scale the data as it is displayed.
You might want to set the allowable pixel values for a processed image so that the
resulting data has certain characteristics, especially if you display the data. For example,
suppose you are using an edge detection filter that theoretically produces data ranging
in value from -1000 to +1000. However, you know that the images you’ll be filtering will
actually yield filtered data ranging from -100 to +100. If you set the allowable values to
match this range and then display the filtered data, the display will be more useful, since
the data will be scaled and stretched out over the framebuffer is meaningful range.
Setting Maximum and Minimum Pixel Values

Minimum and maximum values are image attributes that are stored with an image. You
can set the minimum and maximum allowable values for an image’s pixel data by using
the setMinPixel() and setMaxPixel() functions. Both these functions take an ilPixel
reference as an argument:
ilStatus setMinPixel(const ilPixel& pix);
ilStatus setMaxPixel(const ilPixel& pix);

Use getMinPixel() and getMaxPixel() to query an image about its minimum and
maximum allowable pixel values:
void getMinPixel(ilPixel& pix);
void getMaxPixel(ilPixel& pix);

These functions return the minimum or maximum pixel value by reference.

30

Image Attributes

Setting Maximum and Minimum Pixel Values for a Channel

You can also set the minimum and maximum values for an individual channel of an
image:
ilStatus setMinValue(double val, int c=0);
ilStatus setMaxValue(double val, int c=0);

These functions set channel c’s minimum or maximum value to val.
To query an image about its channel value limits, use getMinValue() and getMaxValue():
double getMinValue(int c=-1);
double getMaxValue(int c=-1);

These functions return the minimum or maximum allowable value for the specified
channel (the default, -1, returns the minimum or maximum of all channels).
Setting Maximum and Minimum Scaling Values For Color Conversion

Minimum and maximum scaling values are used by the IL during color conversion. By
default, the scale minimum and maximum are the same as the image minimum and
maximum values. The IL provides functions you can use to set and retrieve maximum
and minimum scaling values.
The initScaleMinMax() function initializes the scale minimum and maximum to the
image minimum and maximum values. If scale minimum and maximum have already
been set, they are unchanged, unless force is TRUE.
void initScaleMinMax(int force=0);

The function setScaleMinMax() sets the minimum and maximum scaling values to min
and max. The setScaleType() function sets the scale minimum and maximum to the
minimum and maximum values of the data type passed in type.
ilStatus setScaleMinMax(double min, double max);
ilStatus setScaleType(iflDataType type = iflDataType(0));

The getScaleMax() and getScaleMin() functions return the maximum and minimum
values used for scaling during color conversion.
double getScaleMax();
void getScaleMin();

31

Chapter 2: The ImageVision Library Foundation

Data Compression
Often, images stored in a file on disk are compressed to minimize their size. Such images
need to be decompressed before they can be read. There are many different compression
algorithms. Each file format (for example, TIFF) determines which algorithms it
supports. See “Setting a File’s Compression” on page 75 for more information about
which compression algorithms the IL supports. From a programmer’s point of view, as
data is read or written in an IL program, its compression or decompression is handled
transparently.

The Cache
The IL uses the term cache to mean a portion of memory that holds raw and processed
image data that can be accessed by a process. This is not the same as the hardware cache
that is accessed by the CPU. The IL cache holds image data in rectangular pieces called
pages. The cache does not necessarily hold all the pages for each image being processed,
but only those pages that have been referenced and have not been bumped out of the
cache to make room for more recently-referenced pages. Thus, only part of an image may
reside in the cache.
Figure 2-7 shows a cache that contains three images being used by an IL application. The
three rectangles on the left show a logical map of the pages for each image. The shaded
boxes indicate the pages of each image resident in the cache. For example, the raw image
contains four pages, only two of which are in the cache. The rectangle on the right shows
the cache as it might contain the pages from the three images.

32

The Cache

Raw Image
1

Processed Image
1
2
3

Processed Image
1 2
3

2

4

3

1
4
2

4
1
2
3

Figure 2-7

1

2

4

Cache Containing Portions of Three Images

The IL keeps track of the pages in the cache, brings in a page when the program requests
data on a page not in the cache, and chooses a page to be overwritten when additional
room is needed in the cache.
Every IL class that derives from ilMemCacheImg uses the cache and the caching
mechanism defined by ilMemCacheImg. Both ilOpImg and ilFileImg inherit directly
from ilMemCacheImg and use caching in these ways:
•

IL operators (those classes that derive from ilOpImg) use the cache to hold their
output.

•

Classes that derive from ilFileImg place raw, uncompressed data in the cache.

While the cache holds image data in pages, an IL program can access image data in
rectangular blocks of any size, without regard to page boundaries. These rectangular
blocks are referred to as tiles. As shown in Figure 2-8, tiles can cross page boundaries or
can be smaller than a page.

33

Chapter 2: The ImageVision Library Foundation

Image
Page
Tile
Tile

Figure 2-8

Pages and Tiles of Image Data

When a program requests a data tile, the IL checks the cache. If the data corresponding
to the tile is not among the pages already in the cache, the IL brings additional pages into
the cache as necessary. If the cache is already full, it must discard some of the resident
pages in order to make room for the new pages. The page replacement algorithm is based
on a combination of the following factors:
•

The number of times each page has been referenced; the more times a page is
referenced, the more likely it will remain in the cache.

•

The priority of the references; higher priority requests tend to have their pages
retained longer.

•

The relative time since each page was last referenced; the pages that have been in
the cache the longest without being referenced are discarded first.

The overall effect of the page replacement algorithm is that data toward the end of a
chain tends to get preferentially cached. Other data that is frequently referenced (for
instance, the input to an operator whose parameters are being repeatedly adjusted) also
tends to remain in the cache. To prevent data from being recomputed for successive tile
requests, the cache must be large enough so that pages just discarded aren’t reread. (See
the following two sections for more information on setting the size of the cache and
adjusting priorities.)
Since operators place processed image data in the cache, data is operated on as it is
brought into the cache. To maximize efficiency under this execution model, only the
pages needed to satisfy any given tile request are brought into the cache. For example, if
a getTile() request specifies only a single channel of an image that is stored in a separate
format, only the pages containing that channel are accessed. Thus, processing

34

The Cache

multispectral data (or any data stored in a separate format) is made as efficient as
possible.

Managing Cache
By default, the cache size is set to 30% of the total user memory on the host system. The
IL provides two functions to override the default size of the cache, ilSetMaxCacheSize()
and ilSetMaxCacheFraction(), which are defined as shown below:
void ilSetMaxCacheSize(int maxBytes);
void ilSetMaxCacheFraction(float fraction);

The first function sets the cache size to the number of bytes indicated. The second
function computes the size of the cache as the indicated fraction of the total user memory
on the host computer.
You can change these limits without modifying an IL- based program by using the
environment variables IL_CACHE_SIZE or IL_CACHE_FRACTION to set either the size
in bytes or the fraction of user memory, respectively. The IL_CACHE_SIZE value
overrides the value specified by IL_CACHE_FRACTION. Any value established with
these environment variables is overridden by calls to ilSetMaxCacheSize() or
ilSetMaxCacheFraction().
The current value of these cache size limits can be obtained with either
ilGetMaxCacheSize() or ilGetMaxCacheFraction(). The current actual size of the object
is cache can be retrieved with ilGetCurCacheSize(). These functions are defined as
follows:
int ilGetCurCacheSize();
int ilGetMaxCacheSize();
float ilGetMaxCacheFraction();

The IL maintains the global cache in a special memory pool that allows the cache
memory to be compacted to eliminate memory fragmentation problems. When
fragmentation exceeds a defined threshold, the pool is automatically compacted. You can
use the ilSetCompactFraction() function to set the fragmentation threshold to the
maximum fraction of the pool that is allowed to be wasted space before compaction
occurs. The current value of this threshold can be obtained with
ilGetCompactFraction(). The default compaction fraction value is .2 or 20%.
ilSetCompactFraction(float maxWastedFraction);
float ilGetCompactFraction();

35

Chapter 2: The ImageVision Library Foundation

You can force compaction of the pool at any time by calling ilCompactCache(). If the pool
is more fragmented than the fraction passed to this routine, it is compacted. You can pass
zero to cause the pool to be unconditionally compacted.
ilCompactCache(float maxWastedFraction=0);

You can use the getCacheSize() function of ilMemCacheImg to query the cache size for
an individual object:
int getCacheSize();

You can use the flush() member function of ilMemCacheImg to flush the cache for an
individual object:
ilStatus flush(int discard=FALSE));

You can free the memory in the global cache to get it down to a desired maximum size
with ilFlushCache(). This call also compacts the cache memory.
int ilFlushCache(int maxsize);

Priority
The IL assigns priorities to pages in the cache and uses these priorities to make decisions
about which pages to discard. The priority associated with pages in cache ranges from
zero (lowest) to seven (highest); the higher the priority, the greater the likelihood the
page will remain resident.
The IL maintains a linked list of the pages in cache for each of priority levels 0 through
7. Figure 2-9 illustrates this concept. This simplified diagram shows a cache with three
pages at priority level seven, two pages at priority level three, and three pages at level
zero.

36

The Cache

Priority level

Pointers to pages in cache

7
6
5
4
3
2
1
0
Figure 2-9

Priority Lists in Cache

The initial (default) priority level of a page is zero. The following events can cause a
change to the priority level:
•

The priority of a page is incremented each time the page is accessed (for example by
a copyTile() or ilMemCacheImg::executeRequest()). This is essentially a reference
count; the more times a page is referenced, the higher its priority.

•

The priority of the page is incremented when you use the lockPage() method to lock
a page.

•

If you use the setPriority() method to set the priority of the image containing the
page, the IL increases the priority of the page by one plus the value specified in
setPriority() each time the page is referenced. The setPriority() definition is shown
below:
void setPriority(int priority);

•

The maximum number of pages at each priority level is one eighth of the total
number of pages in cache. If the number of pages at any one priority level exceeds
this limit, the priority level of the last page at that level is reduced by one. In other
words, the page is moved to the head of the list at the next lower priority level.

You can use the ilmonitor utility to monitor the activity of pages in cache. See the ilmonitor
reference page or “Image Tools” on page 277 for more information about ilmonitor.

37

Chapter 2: The ImageVision Library Foundation

Page Size
The page size for each operator is defined by its input images. For an ilFileImg, the page
dimensions match those used to store the image on disk. Some images also let you set the
size of the pages in the cache, the data type, and the ordering of the cached data. The data
type and ordering affect how data is cached, so if you change these attributes, you might
also want to change the size of the cache. To set the data type or the ordering of data in
the cache, use the appropriate functions defined by ilImage, setDataType() and
setOrder(). These functions are described in “Image Attributes” on page 20. “Managing
Cache” on page 35 describes how to set the size of the cache.
Only image operators allow you to set the page size of an image. If you change the page
size of an image, you should follow the suggestions in “Cache Priority” on page 250.
To set the size of the pages used in the cache for a particular image, use setPageSize(),
which is defined in the ilImage class as follows:
ilStatus setPageSize(int nx, int ny, int nz, int nc)
{ return setPageSize(iflSize(nx, ny, nz, nc)); }
ilStatus setPageSize(const iflSize& pageSize);
ilStatus setPageSize(int nx, int ny);

The following functions are related:
ilStatus setPageSizeZ(int nz);
ilStatus setPageSizeC(int nc);

The arguments specify the x, y, z, and c dimensions of the page in pixels. This function
calculates the number of bytes needed to store a page with the specified dimensions.
You can use any of the following functions to query an image about its page size,
depending on whether you want the answer in page dimensions, bytes, or pixels:
size_t
size_t
size_t
size_t
size_t

getPageSize();
getPageSize(int& nx, int& ny, int& nz, int& nc);
getPageSize(int& nx, int& ny)
getPageSize(iflSize& pageSize, iflOrientation workOrientation);
getPageDimensions(iflSize& pageSize)

int getPageSizePix();
int getPageSizeVal();

getPageSize()

38

returns the page size in bytes; this function is overloaded, as shown, to
take no arguments or to take arguments that return the size and
orientation of the page in pixels.

The Cache

getPageSizePix()
returns the total number of pixels represented by a page; this value is
found by multiplying the x, y, and z dimensions.
getPageSizeVal()
returns the total number of data elements represented by a page; this
function multiplies the page’s channel dimension by the value returned
from getPageSizePix().

Multi-threaded Paging Support
The ilImage class provides functions to support paging in a multi-threaded environment.
These functions allow you to lock pages to ensure that those pages stay in memory until
you unlock them. The five virtual functions that control paging are:
virtual int hasPages();
virtual ilPage* lockPage(int x, int y, int z, int c,
ilStatus& status, int mode=ilLMread);
virtual void unlockPage(ilPage* page);
ilStatus lockPageSet(ilLockRequest* set,
int mode=ilLMread, int count=1);
void unlockPageSet(ilLockRequest* set, int count=1);

hasPages()

returns TRUE for ilMemCacheImg and all of its descendants and FALSE
for all other classes in the IL. This function is useful for determining
whether the ilImage in question supports paging.

lockPage()

locks down the page located at x, y, z, and c in the cache; it returns a
pointer to that page, which is later passed to unlockPage() to free up that
page.

unlockPage()

frees the page specified by the pointer in the argument list.

lockPageSet()

processes a set of ilLockRequest structures and returns pointers to the
requested pages in the structures.

unlockPageSet()
releases the set of pages obtained by the lockPageSet() function.
These methods provide a mechanism to bypass the overhead of getTile() and setTile(),
but they require that you consider all of the attributes of the page: size, data type, and
order.

39

Chapter 2: The ImageVision Library Foundation

Accessing Image Data
All classes derived from ilImage read, write, and copy image data using the same set of
data access functions defined by the ilImage base class. Each derived class implements
the functions as necessary to suit its particular requirements. A key feature of these
functions is that they allow you to access any arbitrary rectangle, or tile, of image data,
regardless of how that data is stored. This flexibility allows the IL is demand-driven
execution model to be implemented. As part of this model, calls to some of these
functions are generated automatically. However, you can also call these functions
explicitly as needed. The execution model is discussed in detail in “The IL Execution
Model” on page 50. The ilImage class defines both three-dimensional and, for
convenience, two-dimensional data access functions, as shown in Table 2-2.
Table 2-2

Data Access Functions

Three-dimensional

Two-dimensional

Description

getTile3D()

getTile()

reads, writes, and copies a tile of data

setTile3D()

setTile()

copyTile3D() or <getStatus() != ilOKAY) {
printf("file %s could not be opened", fname);
exit(1);
}
// obtain image attributes

72

Using IL to Access an Image

iflDataType theDataType = someFile->getDataType();
int xsize = someFile->getXsize();
int csize = someFile->getNumChans();
// allocate buffer
char* buf = new char[iflDataSize(theDataType, xsize*csize)];
// read data into buffer
someFile->getTile(0, 0, xsize, 1, buf);

In this example:
1.

A file is opened for reading and a corresponding ilFileImg object is created. If the
file cannot be opened, the program exits.

2. The ilFileImg is queried about some of its attributes to determine what size buffer to
allocate for holding one row of the image’s data.
3. The buffer is allocated. The iflDataSize() function returns the number of bytes
needed for the data type indicated by its first argument, multiplied by the optional
second argument. This function is declared in the header file il/iflDataSize.h and
described in “Computing the Size of Data Types” on page 363.
4. The getTile() function reads the first row of the image’s data into the buffer.

Creating an Image File
To create a new image file, you need to specify the characteristics of the data, such as its
data type, and indicate what file format will be used. The ilFileImg constructor creates a
new image file, as follows:
iflFileConfig cfg(iflSize(xsize, ysize));
ilFileImg newFile(“newFileName”, NULL, &cfg);

This constructor creates an image file with the requested size; all of the other attributes
use the default values. The first argument to the ilFileImg constructor specifies the name
of the file to create. The second argument specifies a pointer to an ilImage to use for
default image attributes, In this example, NULL is used which means the file format’s
own preferred defaults are used. The third argument specifies a pointer to an
iflFileConfig argument which is used to specify various image file attributes: the x and y
size of the image in this example.
Here is a more extensive use of the ilFileImg constructor where many more image
parameters are specified. (All of these attributes are discussed in detail in “Image

73

Chapter 3: Accessing External Image Data

Attributes” on page 20, along with the constants that specify particular values for these
attributes.)
iflFileConfig cfg(iflSize(xsize, ysize, zsize, csize), datatype,
dimensionorder, colormodel, orientation, compression,
iflSize(xpsize, ypsize, zpsize, cpsize));
ilFileImg newFile(“newFileName”, srcImage, &cfg, format);

It is rare that you would specify all of these parameters. In fact, it is likely that such a
fully-specified configuration would be in error, for example, the color model and channel
size would have to agree with one another. Normally, only a few attributes are specified;
the remainder would take default values:
iflFileConfig cfg(iflSize(xsize, ysize), datatype,
iflOrder(0), colormodel, iflOrientation(0), iflCompression(0),
iflSize(xpsize, ypsize));
ilFileImg newFile(“newFileName”, NULL, &cfg);

In this example, the dimension order (iflInterleaved, iflSequential, or iflSeparate), the
orientation, for example, iflLowerLeftOrigin, and the compression are allowed to default
to the format’s preferred values: the z size is set implicitly at 1 and the channel size
matches that of the specified color model. The only attributes specified for the image are
its size, its data type, for example, iflUChar, iflFloat, its color model, for example, iflRGB,
iflRGBPalette, and its page size.
The page size argument defines the x, y, z, and c (channel) dimensions of the pages that
the image is broken into as it is stored on disk. The x, y, and z dimensions are specified in
pixels. Paging in the c dimension is specified in channels and is useful for multi-spectral
images with a large number of channels. If no page size is supplied, the default page size
for that particular format and image size is used.
The attributes specified when creating an image file must match those supported by the
file format being used, for example, TIFF files support any data type except iflDouble,
SGI files support only iflUChar and iflUShort, and FIT files can handle any data type. See
the reference pages in for the various image formats supported by IFL for more
information about what they support.
Once you create a file, you can write data to it. The example shown below assumes the
image file, theImg, of size size was previously created. Its data is written to the file,
outFile.tif, using copyTile().
ilFileImg tmpFile(“outFile.tif”, theImg);
tmpFile.copyTile(0, 0, size.x, size.y, theImg, 0, 0);

74

Using IL to Access an Image

Setting a File’s Compression
Often, images stored on disk are compressed to minimize their size. Such images need to
be decompressed before you can read them. There are many different compression
algorithms and each file format determines which algorithms it supports. From a
programmer’s point of view, as data is read or written in an IL program, its compression
or decompression is handled transparently.
The compression attribute indicates which compression algorithm, if any, is used to
compress the data before it is stored on disk. You should not compress files that will be
interactively modified. Modifying portions of a compressed, existing file is dangerous
because the amount of data written must be the same as what was originally in the file.
In general, the size of a file image, once created, is fixed.
To set a file’s compression algorithm, you must specify the compression algorithm when
the file is created. This can be done either by specifying the compression algorithm
explicitly in the iflFileConfig argument that is passed to the ilFileImg constructor or by
inheriting the compression algorithm from another image used for the source image
attributes. Since the set of compression algorithms supported by formats is highly
variable, one of the easiest ways to specify that you want the image compressed is to use
iflCompression(0) which specifies the format’s preferred compression.
The compression specification used in an iflFileConfig is of type iflCompression.
Table 3-1 lists the iflCompression constants currently defined in the header file
ifl/iflTypes.h and their corresponding compression algorithms.
Table 3-1

Compression Algorithms Supported for ilTIFFImg Files

iflCompression Constant

Compression Algorithm

iflCompression(0)

use format’s preferred compression

iflNoCompression

no compression

iflCCITTFAX3

CCITT Group 3 fax encoding

iflCCITTFAX4

CCITT Group 4 fax encoding

iflLZW

Lempel-Ziv and Welch algorithm

iflPACKBITS

Apple® Computer, Inc., Macintosh® RLE (run-length encoding)

iflSGIRLE

SGI’s RLE compression

75

Chapter 3: Accessing External Image Data

Table 3-1 (continued)

Compression Algorithms Supported for ilTIFFImg Files

iflCompression Constant

Compression Algorithm

iflJPEG

Joint Photographic Expert Group

iflZIP

ZIP deflate/inflate

To query an existing file about which compression algorithm it uses, call
getCompression():
iflCompression whichCompression = myFile->getCompression();

This function returns a value of type iflCompression corresponding to one of the
supported algorithms.

Querying a File Image
Once you create an ilFileImg, you can query its attributes with any of the following
functions:
const char* getFileName();
iflFormat* getImageFormat();
const char* getImageFormatName();
int getFileDesc();
int getFileMode();
int getNumImgs();
int getCurrentImg();

Table 3-2 describes each of these functions.
Table 3-2

76

File Query Functions

Function

Description

getFileName()

Returns the name of the file.

getImageFormat()

Returns the file format—TIFF, SGI, PhotoCD Image Pack, PhotoCD
Overview Pack, GIF, or FIT.

getImageFormatName()

Returns the name of the image format.

getFileDesc()

Returns the file descriptor.

Using IL to Access an Image

Table 3-2 (continued)

File Query Functions

Function

Description

getFileMode()

Returns either O_RDWR or O_RDONLY, depending on whether
the file was opened for reading and writing or just reading.

getNumImgs()

Returns the number of images stored in the file.

Setting and Getting Special Image Properties
The iflFile member functions, getItem() and setItem(), deal with format-dependent
name-value pairs, called items, associated with an image within an image file.
Usage of these functions requires format-specific knowledge of the meaning of the tags
for the specific file format, for example, for iflTIFFImg, the meaning of the tags is given
in the TIFF specification.
Using getItem()

The getItem() method returns the value of an item associated with the current image in
the image file.
virtual iflStatus getItem(int tag, va_list ap);

The tag argument specifies the name of the item to be set. It is interpreted by the specific
iflFile subclass. The number and types of the remaining arguments are determined by the
particular subclass of iflFile and the tag value.
The return value is iflOKAY if the function succeeds or an appropriate iflStatus error
value if it fails.
Using setItem()

The setItem() method sets the value of an item associated with the current image in the
image file.
Calling setItem() may change some image attributes. You can check this by calling
haveAttributesChanged() after calling setItem().
virtual iflStatus setItem(int tag, va_list ap);

77

Chapter 3: Accessing External Image Data

The tag argument specifies the name of the item to be set. It is interpreted by the specific
iflFile subclass. The number and types of the remaining arguments are determined by the
particular subclass of iflFile and the tag value.
The return value is iflOKAY if the function succeeds, or an appropriate iflStatus error
value if it fails.
Using haveAttributesChanged()

You use haveAttributesChanged() to determine whether or not image attributes have
changed.
int haveAttributesChanged();

This function returns TRUE if any attribute has changed since the last call to this method,
otherwise, the function returns FALSE.

Importing and Exporting Image Data
IL provides a convenient mechanism for importing or exporting raw image data between
IL and other libraries or devices. This mechanism is encapsulated in the ilMemoryImg
class, which interprets a contiguous array of data residing in memory as an ilImage
object. Since ilMemoryImg inherits from ilImage, you can use any of the data access,
query, and other functions defined in ilImage. In addition, ilMemoryImg defines a
function that returns a pointer to its array of data so that you can read the data (for
exporting) or write new data (for importing). The class ilXImage, derived from
ilMemoryImg, allows you to convert an XImage (an X Window data structure that
defines X’s representation of an image) to an ilImage and vice versa.

Images in Memory
The ilMemoryImg class provides four constructors. You can use these constructors to:

78

•

allocate an array to hold data that will be written

•

use an existing array

•

create an ilMemoryImg object from an ilImage

•

create an empty ilMemoryImg that will be populated later

Importing and Exporting Image Data

The first constructor allocates an array large enough to hold size.x*size.y*size.z*size.c
pixels of the indicated data type:
ilMemoryImg(const iflSize& size, iflDataType datatype,
iflOrder order);

This array is deallocated when the ilMemoryImg object is destroyed.
The second constructor allows you to import data. It takes as an argument an existing
array of data:
ilMemoryImg(void* data, const iflSize& size, iflDataType datatype,
iflOrder order);

This constructor creates an ilMemoryImg object and initializes its data array pointer
with the value passed in data. The size of the specified array is equal to or larger than
size.x*size.y*size.z*size.c pixels of the indicated data type. Since this array was not
allocated by ilMemoryImg, it will not be deallocated automatically when the
ilMemoryImg object is destroyed.
Both of these constructors set the ilMemoryImg’s attributes—size, data type, and
order—to the values passed in the constructor so that you can use the query functions
defined in ilImage, such as getDataType(). The minimum and maximum allowable pixel
values are set by default to the minimum and maximum values allowed for the image’s
data type. In addition, the coordinate space attribute is set to iflLowerLeftOrigin. The
color model is set depending on the number of channels in the image. as shown in
Table 3-3.
Table 3-3

Color Models

channels

color model

1

iflLuminance

2

iflLuminanceAlpha

3

iflRGB

4

iflRGBA

5 or more

iflMultiSpectral

The third constructor takes an ilImage as an argument:
ilMemoryImg(ilImage* img);

79

Chapter 3: Accessing External Image Data

The ilMemoryImg object has the same attributes as the ilImage. These attributes and the
source image data are not changed if the source ilImage changes (thus, you can think of
the ilMemoryImg as taking a snapshot of the ilImage). You can explicitly synchronize
the ilMemoryImg with its source ilImage by calling the sync() method on the
ilMemoryImg.
The fourth constructor returns an ilMemoryImg object with no data or attributes:
ilMemoryImg();

You can use this constructor when you need to create an ilMemoryImg before you can
supply its data. Use setDataPtr() to specify the data.
To change the image data residing in an ilMemoryImg object, call setDataPtr() and pass
a pointer to the new data. You must call setSize() if the new data is a different size than
currently noted for the ilMemoryImg object. Finally, you should also call markDirty() to
indicate that the data in the ilMemoryImg object has been altered.
void setDataPtr(void* data);
ilStatus setSize(const iflSize &size);
void markDirty();

To gain direct access to the image data residing in a ilMemoryImg object, call
getDataPtr(). This function returns a void pointer to the data, as shown below:
void* getDataPtr();

Because an ilMemoryImg resides in memory, you can use it to hold temporary copies of
images that you need to access quickly.
Note: Since the entire image resides in memory, IL’s on-demand execution model is not
used when an ilMemoryImg is accessed.

80

Chapter 4

4. Operating on an Image

Much of the ImageVision Library implementation consists of image-processing
algorithms, or operators. An operator applies its algorithm to the image data encapsulated
in an ilImage object. To maximize the efficiency of the computation required to perform
such an operation, IL uses the demand-driven execution model discussed in Chapter 2,
“The ImageVision Library Foundation.”
This chapter explains how to use each of the operators defined by IL. “Implementing an
Image Processing Operator” on page 215 explains how you can implement your own
image processing algorithm as an IL operator.
This chapter contains the following major sections:
•

“Image Processing Operators Provided with IL” on page 84 describes the set of
approximately 70 image processing operators implemented in IL.

•

“Defining a Region of Interest” on page 153 explains how to mask out portions of an
image and restrict processing to a desired area.

81

Chapter 4: Operating on an Image

IL classes covered in this chapter are mainly those that derive from ilOpImg. The relevant
portion of IL inheritance hierarchy is shown shaded in Figure 4-1.

ilTIFFImg

ilSGIImg

ilFITImg

ilPCDImg

ilPCDOImg

ilMemoryImg
ilImage

ilFileImg
ilCacheImg

ilMemCacheImg

ilGIFImg

ilSpatialImg
ilOpImg
ilPolyadicImg

ilWarpImg

ilMonadicImg

Figure 4-1

ilOpImg and IL Inheritance Hierarchy

The ilOpImg class defines the basic support for all operator classes. It provides functions
for setting attributes, accessing data, setting bias and clamp levels, and propagating
attributes down an operator chain. Most of these functions are declared protected, so
while they are available for use in a subclass’s implementation, they are not available (or
needed) directly. ilOpImg defines only three sets of public functions:
ilStatus setBias(double biasVal = 0);
double getBias();

82

ilStatus setClamp(iflDataType typ=iflDataType(0));
ilStatus setClamp(double min, double max);
int getValidTypes();
int getValidOrders();

Some operators take a bias argument in their constructors and use it in their image
processing algorithms. This bias value is discussed in the sections describing the relevant
operators in the remainder of this chapter. In general, bias is a constant value added to
each pixel luminance value to make it scale correctly. If, for example, the raw pixel
luminance covers values between 100 and 200, some operators are able to scale the
luminance values over the entire depth of pixel luminance values, for example, 0 - 255.
When you scale the luminance values in this way, you need a bias value that adjusts the
initial, raw luminance value, 100, in this example, to zero.
The setClamp() functions allow you to set values that pixels are clamped to if underflow
or overflow occurs. Not all operators allow the clamp values to be modified, so you need
to check that the returned status is not ilUNSUPPORTED if you are assuming you have
changed the values. The first version of setClamp() sets the clamp values to be the
minimum and maximum values allowed for the data type. The default value of typ
means to use the a single bit image type. The second version allows you to specify actual
clamp values. You will not generally need to use either of these functions since most
operators handle overflow and underflow conditions appropriately.
All operators that alter the data range of their inputs compute the worst case minimum
and maximum pixel values to ensure that the processed data can be displayed. For
example, if you multiply two images and then display the result, you can easily end up
with pixel data that is all black. To solve this problem, ilMultiplyImg automatically
computes the worst case minimum and maximum values. When the data is displayed
using ilDisplay, the data is automatically scaled between these values (or those allowed
by the display) so that a meaningful display is produced.
The ilOpImg protected functions that implement these features are
double getInputMin(int idx=0);
double getInputMax(int idx=0);
double getInputScaleMin(int idx=0);
double getInputScaleMax(int idx=0);

The getInput functions return the minimum and maximum luminance values of the
input images. The getInputScale functions return the minimum and maximum
luminance values of the output image.

83

Chapter 4: Operating on an Image

Image Processing Operators Provided with IL
This section discusses all the operators provided with IL. they are grouped functionally
as listed below:

84

•

“Color Conversion and Transformation” on page 85 describes operators that
convert an image from one color model to another.

•

“Arithmetic and Logical Transformations” on page 90 describes operators that
perform pixelwise arithmetic or logical computations.

•

“Geometric Transformations” on page 98 describes operators that warp, rotate, and
zoom (magnify or minify) an image.

•

“Spatial Domain Transformations” on page 106 describes operators that transform
an image in the spatial domain—for example, by sharpening, blurring, convolving,
or rank filtering it in the spatial domain.

•

“Edge Detection” on page 117 describes gradient operators such as compass,
Laplace, Roberts, and Sobel.

•

“Frequency Domain Transformations” on page 120 describes operators that
incorporate forward or inverse Fourier transforms and frequency-domain filters.

•

“Generation of Statistical Data” on page 132 describes the operator that computes
the histogram, mean, and standard deviation of an image.

•

“Radiometric Transformations” on page 136 describes operators that perform
radiometric transformations such as histogram normalization and thresholding.

•

“Combining Images” on page 146 describes operators that blend, merge, or
combine two images.

•

“Constant-valued Images” on page 152 describes an image class that returns a
constant value for all data accesses.

•

“Using a Null Operator” on page 152 describes an operator that performs a “null”
operation.

Image Processing Operators Provided with IL

Color Conversion and Transformation
IL provides several operators that perform color conversions and color transformations
of IL images. These operators can be summarized as follows:
•

The ilColorImg operator converts an existing image from any IL-supported color
model to a requested color model. (See “Color Model” on page 25 for a description
of the color models supported by IL.)

•

Several operators, derived from ilColorImg, convert an existing image to one of the
more commonly used color models: CMYK, grayscale, HSV, and RGB.

•

The ilFalseColorImg operator converts an image from one multispectral color
model to another.

•

The ilSaturateImg operator provides a mechanism to transform the color saturation
of an image.

These color conversion and transformation operators are described in the following
paragraphs. Their positions in IL inheritance hierarchy are shown in Figure 4-2.

ilBGRImg
ilABGRImg
ilSaturateImg
ilCMYKImg

ilGrayImg

...

ilOpImg

ilMonadicImg

ilColorImg
ilHSVImg

ilRGBImg
ilFalseColorImg
ilSGIPaletteImg

Figure 4-2

Color Conversion Operators Inheritance Hierarchy

85

Chapter 4: Operating on an Image

Color Conversion

The base class for the color conversion operators, ilColorImg, defines the generic support
for performing color conversions on image data. It converts data from any supported
color model to any other supported color model, except multispectral.
ilColorImg(ilImage* img, iflColorModel cm);

For example, the following code converts an iflRGB image (theimg) to one whose color
model is iflYCC.
ilColorImg(ilImage* theimg, iflColorModel iflYCC);

The ilColorImg class is not normally used directly to do color-model conversion. Instead,
use derived classes. Each of the six classes derived from ilColorImg performs a specific
conversion. The algorithms used to perform the various conversions are detailed in the
respective reference pages. The six derived classes are summarized below:
•

ilABGRImg converts data to the ABGR color model used by Silicon Graphics’
framebuffer.

•

ilRGBImg converts an image to RGB.

•

ilCMYKImg converts data to the CMYK color model. This color model is used
primarily as an output format for color printers.

•

ilGrayImg converts an image to minBlack.

•

ilHSVImg converts to the HSVcolor model.

•

ilRGBImg converts an image to the iflRGB color model.

•

ilSGIPaletteImg converts data to the iflRGBPalette color model. This color model is
suitable for data that is to be displayed in a color-mapped window.

Using any of these derived classes is simple since the only public member function most
of them define is a constructor. To convert an ilImage, call the constructor for the desired
color model and supply as an argument a pointer to the ilImage to be converted. For the
following example, assume that theImg has already been created and that it uses any one
of the supported IL color models:
ilCMYKImg* cnvrtdImg;
cnvrtdImg = new ilCMYKImg(theImg);

In this example, the constructor for the ilCMYKImg class returns a pointer to an
ilCMYKImg, which produces image data converted to the CMYK color model. Similarly,
the constructors for any of the derived classes—ilABGRImg, ilCMYKImg, ilGrayImg,

86

Image Processing Operators Provided with IL

ilHSVImg, ilRGBImg, or ilSGIPaletteImg—return a pointer to an object of that class,
which produces converted image data. that is really all there is to it.
If you want to convert to the color models for which there is no derived class (iflRGBA,
iflCMY, ilBRG or iflYCC), use the ilColorImg operator.
If an operator image has two or more inputs with different color models, the color model
of the resulting image depends on the color models of the input images. IL converts the
color models of the input images to a common color model before performing the
operation. The resulting image has this color model. You can use the diagram in
Figure 4-3 to determine how IL determines the common color model. Just find the nodes
for the input images and follow the paths from these nodes to a common node. This
nodes determines the color model of the resulting image. For example, if the color
models of two inputs to an operator are iflHSV and iflYCC, the color model of the
resulting image is iflRGB.

iflMultiSpectral

iflRGBA

iflABGR

iflRGB

iflRGBPalette

iflYCC

iflBGR

iflHSV

Figure 4-3

iflMinBlack

iflCMYK

iflMinWhite

iflCMY

Determining the Color Model of Multi-Input Operators

87

Chapter 4: Operating on an Image

ilFalseColorImg

The ilFalseColorImg operator performs false coloring of multispectral images. It
accomplishes this by computing the weighted sum of the input channels for each channel
of the resulting false-color image. The constructors for ilFalseColorImg, except the NULL
constructor, or take a pointer to the input image and the arguments that define the
conversion algorithm:
ilFalseColorImg();
ilFalseColorImg(ilImage *img, int numColumns, int numRows,
const float* xformMatrix, const float* bias=NULL);

The conversion is defined by the transformation matrix, xformMatrix. This matrix has
dimensions numColumns x numRows. Each row of this matrix defines a set of weights
used to produce one channel of the output. Each weight is multiplied by the pixel values
in the corresponding input channel, and the weighted sum forms the output channel.
The conversion may also include a bias vector, bias. This vector contains a constant value
for each input channel that is added to each input value before it is weighted. Thus, the
transformation equation for each channel of the output image is:
Output Cx1 = T CxR ( Input Cx1 + B Cx1 )
where C and R are numColumns and numRows, respectively.
An image transformed by ilFalseColorImg appears in Figure 4-4.

88

Image Processing Operators Provided with IL

Figure 4-4

A Falsely Colored Image

ilSaturateImg

This operator performs a color saturation of its input. If the input color model is not RGB,
the input is first converted to RGB. The constructor for ilSaturateImg takes a pointer to
the input image and an initial saturation value:
ilSaturateImg(ilImage* img=NULL, float sat=1);

The transformation is defined as:
Equation 1

lum = .3redin + .59greenin - .11bluein
Equation 2

redout = lum + (redin - lum)sat

89

Chapter 4: Operating on an Image

Equation 3

greenout = lum + (greenin - lum)sat
Equation 4

blueout = lum + (bluein - lum)sat
You can set the saturation value interactively with setSaturation():
void setSaturation(float saturation);

The current value of the saturation factor can be queried with getSaturation():
float getSaturation();

A value of zero completely desaturates the image (equivalent to ilGrayImg), a value of
one leaves the image unchanged, and values greater than one increase the color
saturation of the image. Output values are clamped to the minimum and maximum
values of the operator image, which by default are simply inherited from the input.

Arithmetic and Logical Transformations
There are numerous IL operators that perform pixelwise arithmetic transformations of
image data. Some of these require two input images—for example, to add them
together—while others perform computations on a single image’s data, such as
determining the absolute value. In the inheritance hierarchy shown in Figure 4-5,
operators that inherit from ilPolyadicImg take two images as inputs and those that
derive from ilMonadicImg take only one.

90

Image Processing Operators Provided with IL

ilAbsImg

...

ilOpImg

ilNegImg

ilInvertImg

ilMonadicImg
ilPowerImg
ilLogImg
ilLutImg

ilArithLutImg
ilSquareImg
ilExpImg

...

Figure 4-5

ilXorImg

ilBlendImg

ilMultiplyImg

ilAbsImg

ilDyadicImg

ilAddImg

ilSubtractImg

ilDivImg

ilMaxImg

ilMinImg

ilAndImg

ilOrImg

ilSqRootImg
ilHistLutImg

ilPolyadicImg

Arithmetic and Logical Operators Inheritance Hierarchy

When using one of the dual-input operators, you might want to use an ilConstImg as one
of the inputs. An ilConstImg returns the same value for all of its pixels, so you can use it
to multiply each of an image’s pixels by a constant value, for example. For more
information on how to create an ilConstImg, see “Constant-valued Images” on page 152.
Single-input Operators

The single-input arithmetic operators are listed in Table 4-1, along with the operation
they perform on each pixel of image data and the pixel data types each operation can

91

Chapter 4: Operating on an Image

produce. The last five operators in Table 4-1 (ilSquareImg, ilSqRootImg, ilExpImg,
ilPowerImg, and ilLogImg) descend directly from ilArithLutImg. The ilArithLutImg
abstract class optimizes the performance of operators that derive from it by pulling
precomputed square, square root, exponent, power, and log values from a lookup table.
This is much more efficient than computing values on a per-pixel basis.
The ilArithLut class in turn inherits from ilLutImg. Consequently, the last five operators
in Table 4-1 inherit the ability to be accelerated further in the CPU or in specialized
graphics hardware. See “Radiometric Transformations” on page 136 and “Using
Hardware Acceleration” on page 253 for details about ilArithLutImg and hardware
acceleration, respectively.
Table 4-1

Single-input Arithmetic Operators and Their Valid Output Data Types

Operator

Operation Performed

Valid Data Types

ilAbsImg

absolute value

iflUChar, iflUShort, iflULong,
iflFloat, iflDouble

ilNegImg

two’s complement

any signed data typea

ilInvertImg

one’s complement

iflBit, iflChar, iflUChar, iflShort,
iflUShort, iflLong, iflULong

ilSquareImg

(pixelvalue)2

any type except iflBit

ilSqRootImg

any type except iflBit

ilExpImgb

base(pixelvalue)

any type except iflBit

ilPowerImgb

(pixelvalue)power

any type except iflBit

ilLogImgb

logbase(pixelvalue)

any type except iflBit

a. iflChar iflShort, iflLong, iflFloat, and iflDouble are the signed data types.
b. These operators allow you to apply scale and bias values to the pixelvalue, so that it becomes
scale*pixelvalue+bias.

An example of processing by an arithmetic operator is given in Figure 4-6, which shows
an original image constructed from simulation data processed with ilNegImg.

92

Image Processing Operators Provided with IL

Figure 4-6

A Positive and Negative Image Pair

The only public member function defined in ilAbsImg, ilNegImg, ilInvertImg,
ilSquareImg, and ilSqRootImg is a constructor that takes a single argument, the input
image. Thus, to include any of these operators in a chain, you simply call its constructor
and pass, as the argument, a pointer to the input ilImage. In this example, assume that
inputImg is a pointer to an already existing ilImage:
ilAbsImg* someAbsImg = new ilAbsImg(inputImg);

The constructors for the ilAbsImg, ilNegImg, ilInvertImg, ilSquareImg, and ilSqRootImg
classes all return a pointer to the operator image.
The constructors for the remaining three classes—ilExpImg, ilPowerImg, and
ilLogImg—take three additional arguments, all of type double. The second argument for
each of these constructors specifies base or power, the third specifies scale, and the fourth
bias.
ilExpImg(ilImage* inImg = NULL, double expBase=0,
double scl=1., double bs=0.);
ilPowerImg(ilImage* inImg = NULL, double pow = 2,
double scl=1., double bs=0.);
ilLogImg(ilImage* inImg = NULL, double logBase=0,
double scl=1., double bs=0.);

The ilExpImg, ilPowerImg, and ilLogImg classes define a function for setting the value
of the second parameter after the operator is created, so that you can dynamically alter
the computation:
void setBase(double expBase=0);
void setPower(double power=2);
void setBase(double logBase=0);

// for ilExpImg
// for ilPowerImg
// for ilLogImg

93

Chapter 4: Operating on an Image

Dual-input Operators

As their names suggest, the dual-input operators ilAddImg, ilSubtractImg,
ilMultiplyImg, and ilDivImg perform standard arithmetic computations—addition,
subtraction, multiplication, and division of two images. The constructors for each of
these classes take as arguments pointers to the two input images, which can be different
sizes but must have the same number of channels. If they are different sizes, by default
the output image is the larger of the two sizes; the smaller input image is padded with
its fill value, and then the operator performs its computation on corresponding pixels in
the two images. You can explicitly set the desired output size with ilImage.setSize().
You may also offset one image with respect to the other using the following
ilPolyadicImg methods:
void setOffset(int x, int y, int z = 0, int input = 0);
void getOffset(int &x, int &y, int &z, int input = 0);

setOffset() offsets the first image with respect to the second by x, y, and z if input is 0. If
input is 1, the second image is offset with respect to the first. getOffset() queries the
dual-input operator for its offsets. If input is 0, the offset of the first image relative to the
second is given; if input is 1, the offset of the second image relative to the first is given.

94

Image Processing Operators Provided with IL

Here are the constructors for the dual-input operators:
ilAddImg(ilImage* in1 = NULL, ilImage* in2 = NULL,
double bias=0);
ilSubtractImg(ilImage* in1 = NULL, ilImage* in2 = NULL,
double bias=0);
ilMultiplyImg(ilImage* in1 = NULL, ilImage* in2 = NULL);
ilDivImg(ilImage* in1 = NULL, ilImage* in2 = NULL, ckDiv=1);

ilAddImg adds the bias value to the sum found by adding the corresponding pixels of
in1 and those of in2. The ilSubtractImg operator subtracts the corresponding pixels of in2
from every pixel of in1 and then adds the bias value. ilMultiplyImg multiplies the pixels
in the two input images, and ilDivImg divides the pixels of in1 by the corresponding
pixels of in2. All of these operators can produce an image containing any data type except
iflBit. An example using ilAddImg appears in Figure 4-7. The two original images appear
as well; one is the flipped version of the other.
The ckDiv argument for ilDivImg’s constructor specifies whether the operator should
check for division by zero. By default, it does check and responds as described below:
•

If the divisor is zero and the dividend is positive, the quotient is set to the maximum
value possible for the final image’s data type.

•

If the divisor is zero and the dividend is negative, the quotient is set to the
minimum value possible for the final image’s data type.

•

Zero divided by zero produces a zero.

You can use setCheck() to change whether this check is made.

95

Chapter 4: Operating on an Image

Original 1

Original 2

Added Images

Figure 4-7

Adding Two Images

The two classes ilMaxImg and ilMinImg compare each corresponding pixel in the two
input images and select the greater or the lesser value, respectively. Their constructors
take pointers to the two input images as arguments. These input ilImages must have the
same number of channels. The output image can contain any data type except iflBit.
(There are also simple, in-line functions defined in the header file il/ilMinMax.h that
compare two values and return the greater or the lesser one. See “Minimum and
Maximum Comparisons” on page 364 for more information about these functions.) An

96

Image Processing Operators Provided with IL

example of using ilMinImg appears in Figure 4-8. Two original images are shown,
followed by the image that results if you apply ilMinImg to these images.

Original Image

Original Mask

Minimum of Image and Mask

Figure 4-8

Minimum of Two Images

Similarly, the logical-operator classes—ilAndImg, ilOrImg, and ilXorImg—perform their
computations (logical AND, OR, and exclusive-OR) by combining each corresponding
pixel in the two input images. The constructors for these classes take pointers to the two
input images as arguments. The input ilImages must have the same number of channels;
the output image can contain any of the following data types: iflChar, iflUChar, iflShort,
iflUShort, iflLong, or iflULong. Figure 4-9 shows an example of using ilAndImg and
ilOrImg on the original images from Figure 4-7.

97

Chapter 4: Operating on an Image

Original Image 1

Original Image 2

Logical AND

Logical OR

Figure 4-9

Logical AND and OR of Two Images

Geometric Transformations
The heart of a geometric transformation, or warp, is the algorithm that maps output
image coordinates to input coordinates. (See Figure 4-10.) The general support for such
transformations is encapsulated in the abstract class, ilWarpImg. Classes that derive from
ilWarpImg— ilTieWarpImg, and ilRotZoomImg—implement specific warping
algorithms;. These algorithms are most efficient for images that are relatively square.

98

Image Processing Operators Provided with IL

Figure 4-10

A Warped Image

The warping classes are shown in Figure 4-11 and discussed in the following sections.

ilRotZoomImg

...

ilOpImg

ilWarpImg
ilTieWarpImg

ilPerspWarp

...

ilWarp

ilPolyWarp
ilAffineWarp

Figure 4-11

Geometric Operator Inheritance Hierarchy

Warping an Image

The ilWarpImg class, from which ilTieWarpImg, and ilRotZoomImg derive, performs up
to a two-dimensional, seventh-order warp. The output image space is mapped to the
input image space with a transformation defined by two sets of polynomials (which can
be up to seventh order), one for the x-dimension and one for the y-dimension. Since the
coefficients for the polynomials are not always integers, the addresses computed for the
output space sometimes contain fractional components. Therefore, a resampling method
must be applied to convert these fractional addresses into meaningful pixel locations.

99

Chapter 4: Operating on an Image

To use ilWarpImg, you must choose a resampling algorithm and specify the coefficients
of the warping polynomials. The constructor takes as its arguments a pointer to the input
image and a constant that corresponds to a resampling method:
ilWarpImg(ilImage* img=NULL, ilResampType rs=ilNearNb,
ilWarp* warp=NULL);

The ilResampType enumerated type is defined in the header file il/iflDataTypes.h and
shown in “Resampling Methods” on page 100. It has these six members:
•

ilNearNb (nearest neighbor)

•

ilBiLinear

•

ilBiCubic

•

ilMinify

•

ilUserDef (for a resampling algorithm you implement)

If you choose a bicubic resampling method, you can use setBicubicFamily() to fine-tune
its algorithm.
ilWarpImg performs output-driven image warps. It uses the abstract. helper class,
ilWarp, to define the specific nature of a given warp. An image of any data type may be
given as input. The proper data conversions will be performed to ensure output is one of
the following valid data types: ilUChar, ilUShort, ilShort or ilFloat.
ilWarpImg is a cached, image operator. It may be linked into operator chains.
Resampling Methods

The ilWarpImg class supports five built-in resampling methods:

100

•

nearest neighbor

•

bi-linear (the default)

•

bi-cubic interpolation

•

filtered minification (ilMinify)

•

auto resampling

Image Processing Operators Provided with IL

The resampling type can be altered with setResampType(). ilWarpImgSetResampType().
Support for user-defined resampling methods is also provided by the setResampFunc()
function.
Nearest neighbor is the fastest method, but produces the lowest quality result. This
method merely copies the value of the input pixel that is closest to the computed address.
It is most useful when performance is more important than image quality, as for instance
when the warp is under interactive control by a human. When the warping parameters
have been adjusted to satisfaction, the final output might be produced with the bi-linear
or bi-cubic method.
The bi-linear method interpolates over a 2x2 neighborhood around the computed input
address, using a simple weighted average. This method is somewhat slower than nearest
neighbor, but produces a much higher quality result.
The bi-cubic method interpolates over a 4x4 neighborhood, using an interpolation kernel
that approximates a two-dimensional bi-cubic spline. For a given (x, y) point, the
interpolation is performed by first interpolating four lines starting at floor(y)-1 and
ending at floor(y)+2; each line runs from floor(x)-1 and ends at floor(x)+2. The resulting
values are then processed vertically to produce the resulting output point.
In order to speed up the processing, the cubic convolution co-efficients are precomputed
to a 1/256 pixel accuracy and stored in a table. This provides more than adequate
accuracy for geometric precision. The co-efficient generation is from equation (8) in the
paper:
Mitchell, D. and A. Netravali, “Reconstruction Filters in Computer Graphics.” Computer
Graphics, Vol. 22, No. 4, pp. 221-228.
The setBicubicFamily() function allows the B and C co-efficients of equation (8) in the
cited paper to be defined, allowing a choice of various bicubic resampling.
Filtered minification is used when unaliased minification is desired. The input image is
filtered and minified. The user can specify a filter or, if none is specified, a box filter is
used. The size of the box filter, depends on the minification factor and it ensures that the
entire input image is sampled. If the box filter or kernel is used, the operation can be
speeded up by sub-sampling the kernel. By using the setMaxSamples() function, the
number of image pixels are averaged to produce an output pixel can be set. So if the
number of samples is set to 10, even when using a 5 x 5 kernel, only 10 image pixels used
to compute the filtered result.

101

Chapter 4: Operating on an Image

Note: When specifying your own kernel, each zero value in the kernel results in one less

multiply/add computation. So, sprinkling zeros around the kernel achieves subsampling.
If you choose the ilMinify resampling method, you can use setMinifyKernel() to specify
your own kernel instead of the default box (all 1s) kernel. In the default case, the kernel
size is dynamically adjusted so that the entire input is sampled (that is, all the input
image pixels are used to compute the output). If you use the default kernel, you can
speed up the operation by using setMaxSamples() to set the number of input image
pixels to be averaged to produce a single output pixel. For example, if you set the
maximum number of samples to 10 and you are minifying by a factor of 8, thus
necessitating the use of an 8 x 8 kernel, only 10 input pixels (instead of 64) uniformly
interspersed throughout the 8 x 8 area are averaged to produce one output pixel.
To define your own resampling method, use setResampFunc() and pass in a pointer to
your algorithm. The reference page for ilWarpImg explains what the supported
algorithms are, which one you might want to use, and how to define your own
algorithm.
You can dynamically change and retrieve the resampling method with setResampType()
and getResampType(), which are inherited from ilWarpImg:
void setResampType(ilResampType rs);
ilResampType getResampType();

Additionally, ilWarpImg lets you determine the amount of error allowed in a warp
performed in graphics hardware with setAddressError(). Its one parameter,
maxPixelsOff, determines by how many pixels the warped data may be incorrect. The
previously set parameter can be retrieved with getAddressError():
void setAddressError(float maxPixelsOff);
float getAddressError();

For backward compatibility, you can define the coefficients of the warping polynomial
using the ilPolyWarpImg.setCoeff() function:
void setCoeff(const ilCoeff_2d& xcoeff, const ilCoeff_2d& ycoeff);

You can query the ilWarpImg object for its coefficients with ilPolyWarpImg.getCoeff()
and for the order of its polynomial with ilPolyWarpImg.getPolyOrder():
void getCoeff(ilCoeff_2d& xcoeff, ilCoeff_2d& ycoeff);
int getPolyOrder();

102

Image Processing Operators Provided with IL

The ilPolyCoeff2SD structure contains floating point numbers for the coefficients. It is
defined in the header file il/ilPolyDef.h, as shown below:
struct ilPolyCoeff2D {
float con,
y, x,
y2, xy, x2,
y3, xy2, x2y, x3,
y4, xy3, x2y2, x3y, x4,
y5, xy4, x2y3, x3y2, x4y, x5,
y6, xy5, x2y4, x3y3, x4y2, x5y, x6,
y7, xy6, x2y5, x3y4, x4y3, x5y2, x6y, x7;
};

The ilTieWarpImg class performs a two-dimensional warp, but it does not allow you to
specify the coefficients of the warping polynomial directly. Instead, you specify pairs of
tie points in the input and the output images that should match after the image is warped
as shown in Figure 4-12. The coefficients of the polynomial, which you can choose to be
first- to seventh-order, are then computed from these tie points. The minimum number
of pairs of points necessary to determine the coefficients of a polynomial of order ord is
given by the formula:
( ord + 1 ) ( ord + 2 )
pairs = -------------------------------------------------2
Thus, you need to specify at least three pairs of points for a first-order polynomial, six
pairs for a second-order, and so on.
The constructor for ilTieWarpImg takes the same arguments as that for ilWarpImg. After
creating an ilTieWarpImg operator, you must specify the tie points from which the
warping polynomial is computed. For this, use setTiePoints):
void setTiePoints(const iflXYfloat* uv,
const iflXYfloat* xy, int n);

This function takes pointers to arrays of n tie points in the input image (xy) and the
output image (uv) and computes the polynomial’s coefficients. (The data type iflXYSfloat
is defined in the header file il/iflCoord.h as an (x, y) coordinate pair of data type float.) The
function isWellDefined() can be used to check if the polynomial coefficients can be
computed from the specified tie points. If the polynomial is successfully computed, one
is returned; if not, zero is returned. Before you call setTiePoints(), you might want to set
the order of the polynomial that will be computed by calling setPolyOrder() and passing
in 1, 2, 3, 4, 5, 6, or 7 as the desired order. If you do not explicitly set the order, a first-order

103

Chapter 4: Operating on an Image

polynomial is used. The function getPolyOrder() returns the order of the warping
polynomial.
To move the tie points, use moveTiePoint(), defined as follows:
ilStatus moveTiePoint(float u, float v, float x, float y, int idx);

ilWarpImg defines functions (which ilTieWarpImg and ilRotZoomImg inherit) that,
given a point in the input (or output) image, compute the corresponding point in the
output (or input) image, using the mapping specified by the polynomial:
void evalUV(iflXYfloat& uv, const iflXYfloat& xy);
void evalXY(iflXYfloat& xy, const iflXYfloat& uv);

The function evalUV() takes the input image point xy and returns by reference the
corresponding point uv in the output image. Similarly, evalXY() computes the input
image point, xy, from the output image point, uv.
Figure 4-12 shows the result of applying ilTieWarpImg to an image.

Original Image

Figure 4-12

104

Warped Image

Warping an Image

Image Processing Operators Provided with IL

Rotating, Zooming, and Flipping an Image

Unlike the various warping classes, the ilRotZoomImg operator is limited to performing
two-dimensional affine transformations on an image. This single operator can rotate,
zoom (magnify or minify), and mirror (or flip) image data:
ilRotZoomImg(ilImage* img = NULL, float rotAngle=0,
float horizontalZoom=1, float verticalZoom=1,
ilResampType rs=ilNearNb);

The input image, img, is rotated by rotAngle degrees in a counterclockwise direction and
magnified or minified in the appropriate dimension by the horizontalzoom and
verticalzoom factors. The default resampling method is nearest neighbor (ilNearNb). This
method, when there is no hardware acceleration, chooses ilMinify resampling for pure
minification (x and y zoom factors < 1.0 and rotation angle = 0.0) and ilNearNb
otherwise. If there is hardware acceleration, ilBiLinear is chosen for pure minification
and ilNearNb otherwise. This operator is especially efficient when the rotation is a
multiple of 90 degrees and when the resampling method is ilNearNb.
Functions are provided for you to dynamically change all the parameters:
void
void
void
void

setAngle(float rotAngle);
setZoom(float horizontal, float vertical);
setZoom(float zoom);
setCenter(float h, float v);

An analogous set of functions is provided to retrieve the parameters:
float getAngle();
void getZoom(float& horizontal, float& vertical);
int getCenter(float& h, float& v);

You can also select a portion of the image to be operated on by using setSize() (inherited
from ilImage) and setCenter(). Alternatively, you can ask for only the desired portion
using getTile() or copyTile() with the appropriate arguments, or you can define a region
of interest.
The setSize() and setCenter() functions limit the transformation to the area specified
with setSize(), centered on the point given in setCenter(). The center point is specified in
the input image’s coordinate space. These functions also translate the image’s coordinate
space so that the image’s origin becomes the corner of the region specified by setCenter()
and setSize(). You can clear the center point set with setCenter() by calling clearCenter().

105

Chapter 4: Operating on an Image

You can zoom the input image to a particular size by calling sizeToFit():
void sizeToFit(float width, float height,
int keepAspect=FALSE);

You specify the desired image width and height with width and height. If you want the
image to keep its aspect ratio, set keepAspect to TRUE. The default behavior allows the
image’s aspect ratio to change.

Spatial Domain Transformations
Spatial operators transform image data by computing a weighted sum of the pixels in the
neighborhood surrounding the target pixel. The size of the neighborhood and the
weights used for neighboring pixel values are defined by the kernel. Some spatial
operators predefine their kernels while others allow the user to specify them. In addition,
a method for handling pixels at the edge of an image must be specified, since a pixel’s
neighborhood is undefined beyond the edge of a page. The spatial operators provided
with IL are shown in Figure 4-13.

ilBlurImg
ilConvImg

ilSharpenImg
ilSepConvImg

ilDilateImg

...

ilOpImg

ilSpatialImg

ilErodeImg
ilMaxFltImg
ilRankFltImg

ilMedFltImg

ilMinFltImg

Figure 4-13

106

Spatial Domain Operator Inheritance Hierarchy

ilGBlurImg

Image Processing Operators Provided with IL

The ilSpatialImg class, which is an abstract class, defines the basic support for spatial
operators that derive from it. The public functions it defines are those that allow you to
set and retrieve the kernel and the edge-handling method.:
void
void
void
void

setKernel(ilKernel* kern=NULL);
setKernelSize(int x, int y, int z=1);
getKernelSize(int& x, int& y, int& z);
getKernelSize(int& x, int& y);

void setEdgeMode(ilEdgeMode eMode = ilPadSrc);
ilEdgeMode getEdgeMode();

Note: Some operators predefine their kernel and thus do not allow you to set it.

The ilKernel class defines a kernel as consisting of the following elements:
•

the size of the kernel in the x, y, and z dimensions

•

the size of the data type used to specify kernel weights

•

a pointer to the data specifying the weights

The x, y, and z dimensions should be odd numbers so that a neighborhood can be exactly
centered on a single, target pixel. If they are even numbers, the data may be shifted. See
the reference page for ilKernel, il/ilKernel.h, and “Auxiliary Classes” on page 358 for more
information about this class.
The origin of an ilKernel normally falls at its center pixel. The origin can be specified with
ilKernel’s setOrigin() function to correspond to any of the pixels in the kernel. The
arguments x, y, and z indicate the origin’s offset from the upper-left-front corner of the
kernel. getOrigin() returns the offset by reference.
void setOrigin(int x, int y, int z=0);
void getOrigin(int &x, int &y, int &z);

ilSpatialImg’s setEdgeMode() function specifies how the neighborhood is defined for
pixels at the edge of the image. Explanations of the supported edge modes, which are
defined in ilTypes.h, follow:
ilReflect

Sufficient data near the edge of the image is reflected so that a full-sized
output image can be processed without producing artifacts at the image
edge. This mode gives the best results for most operators.

ilWrap

Sufficient data is taken from the opposite edge of the source image so
that a full-sized output image can be processed.

107

Chapter 4: Operating on an Image

ilPadSrc

The edge of the input image is padded with the input image’s fill value
so that a full-sized output image can be processed (see Figure 4-14). See
“Fill Value” on page 29 for more information on an image’s fill value.

Image Data
Kernel
Page Being Processed

Filled Border

Padded Data

Figure 4-14

The ilPadSrc Edge Mode

ilNoPad

No padding is done, and the output image shrinks by the size of the
kernel minus one in each dimension.

ilPadDst

Similar to ilNoPad, except that the output, image’s border is sufficiently
padded with its fill value so that the final image is the same size as the
source image.

Convolving an Image

The ilConvImg operator performs general image convolution. This class is not an
abstract class, so you can use it directly to convolve image data. The constructor for
ilConvImg, which is its only public member function, is shown below:
ilConvImg(ilImage* inputImage=NULL,
ilKernel* inputKernel=NULL, double biasVal = 0.,
ilEdgeMode eMode=ilPadSrc);

This function takes a pointer to the source or input image, a pointer to the kernel, and an
enumerated type that matches one of the supported edge modes. The other argument,
biasValue, is added to the weighted sum (image data multiplied by kernel weight) for
each neighborhood. You can set the bias value with the setBias() function.
You can also perform certain convolutions more efficiently with a separable kernel (one
that is specified by row and column vectors). ilSepConvImg, descended from
ilSpatialImg, provides this feature. Its constructor accepts the input image, the row and
column kernels, the sizes of the kernels, an optional bias value, and an optional edge
mode:

108

Image Processing Operators Provided with IL

ilSepConvImg(ilImage *inputImg = NULL,
float *xkernel=NULL, float *ykernel=NULL, int xsize=1,
int ysize=1, double biasVal=0.0,
ilEdgeMode eMode = ilPadSrc)
float *zkernel = NULL, int zsize = 1);

As shown, the default bias is 0.0, and the default edge mode is ilPadSrc. The default
kernel size for each kernel is 5. This operator is especially efficient for kernel sizes 3 x 3,
5 x 5, and 7 x 7.
ilSepConvImg also defines a set of functions to set and get the kernel vectors:
void setXkernel(float *xval);
void setYkernel(float *yval);
void setZkernel(float *zval, int n = 0);
float* getXkernel();
float* getYkernel();
float* getZkernel();

setXkernel() allows you to change the row kernel; getXkernel() returns its value.
setYkernel() allows you to change the column kernel; getYkernel() returns its value.
setZkernel() allows you to change the depth kernel; getZkernel() returns its value. If you
replace any kernel with one that has a different size, use ilSpatialImg.setKernelSize()
(inherited from ilSpatialImg) to update the sizes.
Blurring or Sharpening an Image

The two blurring operators, ilBlurImg and ilGBlurImg, both blur an image by
performing a convolution, but they use different kernels and algorithms for the
convolution. ilBlurImg convolves the image with a blurring kernel using the general
convolution algorithm defined by ilConvImg. ilGBlurImg (descended from
ilSepConvImg) convolves an image with a separable two-dimensional Gaussian kernel.
Because ilGBlurImg uses a separable kernel, it is generally more efficient than ilBlurImg.
Although different methods are used, often the blurred results do not look significantly
different. The reference pages for these classes provide more detailed information on the
kernels and convolution algorithms used. Figure 4-15 shows an original image that is
used as an example in the following pages.

109

Chapter 4: Operating on an Image

Figure 4-15

An Original Image

The ilBlurImg and ilGBlurImg classes have slightly different interfaces:
ilBlurImg(ilImage *img = NULL, float blur=1.,
float radius=2., ilEdgeMode e=ilPadSrc);
ilGBlurImg(ilImage *inputImg = NULL,
float blur = 1.0, int xsize = 5, int ysize = 5,
double biasVal = 0., ilEdgeMode eMode = ilPadSrc);

Both constructors take as arguments a pointer to the source image, a blur factor ranging
from 0.0 (no blur) to 1.0 (maximum blur), and an enumerated type specifying the edge
mode. By default, the blur factor is set to 1.0 and the edge mode is ilPadSrc. The radius
argument for ilBlurImg (with a default value of 2.0) and the xsize and ysize arguments for
ilGBlurImg (with default values of 5) control the size of the kernel used for blurring. (The
ilBlurImg kernel size is equal to 1+radius*2.) ilGBlurImg’s biasValue argument, which by
default is zero, is added to the final weighted sum.
Both classes allow you to dynamically modify the amount of blur by passing a float value
to the setBlur() function. You can also change the size of the kernel with setBlurRadius()
(for ilBlurImg) or setBlurKernelSize() (for ilGBlurImg). An image blurred with
ilBlurImg is shown in Figure 4-16.

110

Image Processing Operators Provided with IL

Figure 4-16

An Image Blurred with ilBlurImg

The ilSharpenImg class is similar to ilBlurImg, except that instead of using a kernel that
blurs, it uses a kernel that sharpens the image data. Its constructor takes a similar set of
arguments:
ilSharpenImg(ilImage *img = NULL, float sharpness=.5,
float radius=1.5,ilEdgeMode e=ilPadSrc);

The sharpness factor indicates the degree of sharpening that should occur. This factor can
have a value between 0.0 and 1.0, with a default value of 0.5. A sharpened image appears
in Figure 4-17.

111

Chapter 4: Operating on an Image

Figure 4-17

An Image Sharpened with ilSharpenImg

As with ilBlurImg, you can dynamically change the sharpness factor (with
setSharpness()) and the size of the radius (with setSharpenRadius()). getSharpness()
and getSharpenRadius() are the query methods that return the values of the sharpness
factor and radius. Making the size of the radius too large or repeatedly cycling an image
through the sharpening operation can result in a grainy, high-contrast image. Figure 4-18
shows an example of this.

Figure 4-18

112

An Over-sharpened Image

Image Processing Operators Provided with IL

To see additional illustrations of the ilBlurImg and ilGBlurImg transformations, refer to
“Spatial Domain Transformations” on page 401.
Rank Filtering an Image

The ilRankFltImg class performs two-dimensional rank filtering, which is typically—
though not exclusively—done on black-and-white images. It involves sorting all the
pixel values (for each channel) for a neighborhood of pixels. Then, the target pixel is
assigned the values corresponding to a specified rank. For example, suppose you have
chosen a 3 x 3 neighborhood and a desired rank of 0 (the minimum). In this case, each
pixel is assigned the lowest value found among itself and its eight surrounding pixels.
The classes that derive from ilRankFltImg—ilMinFltImg, ilMaxFltImg, and
ilMedFltImg—assume that the desired rank is the minimum possible rank, the
maximum possible rank, and the median, respectively. Median filtering is useful for
removing binary, or impulse, noise in image data. Minimum and maximum rank filtering
produce morphological erosion and dilation. An example of an image processed with
ilMedFltImg appears in Figure 4-19.
The only public member function defined by these three classes is a constructor, and each
of these constructors takes the same set of arguments. ilMinFltImg’s constructor is shown
below:
ilMinFltImg(ilImage* inputImage = NULL,
ilEdgeMode edge=ilPadSrc, ilKernel* inputKernel=0);

As shown, you need to specify the input image, how pixels at the edge of the image are
to be handled, and the kernel. The kernel is treated as a mask. Only nonzero elements are
included in the neighborhood; the rest are ignored, as are the kernel weights.
The constructor for the ilRankFltImg superclass takes the same set of arguments and an
additional one for specifying the desired rank for the target pixel:
ilRankFltImg(ilImage* inputImage = NULL, int filterRank = -1,
ilEdgeMode eMode = ilPadSrc, ilKernel* inputKernel=NULL);

The default rank of minus 1 indicates that median rank should be used. You can
dynamically change the desired rank with the setRank() function. You can also
determine what the maximum possible rank is with getMaxRank().

113

Chapter 4: Operating on an Image

Original

Figure 4-19

Filtered

Median Rank Filtering on an Image

To see additional illustrations of the rank filtering transformations, refer to “Spatial
Domain Transformations” on page 401.
Morphological Operators

Morphological operators include shape-dependent, nonlinear image transformations
such as erosion and dilation. The operators implemented in IL, ilDilateImg and
ilErodeImg, can be used on 1-D, 2-D or 3-D data sets. More powerful morphological
operations such as “opening” and “closing” can be performed by chaining together
dilation and erosion operations. Opening can be accomplished by an erosion followed by
a dilation. Closing can be done with a dilation followed by an erosion.
These operations are defined on binary or grayscale images. Note that you can operate
on color images if you remember that “binary” and “grayscale” indicate how the pixel
values or intensities in each channel of the image are interpreted. A binary image
contains no more than two levels or intensity values: zero and not zero. An 8-bit image
with 256 pixel intensities can be treated as a binary image by collapsing the intensities
into two groups, for example, a zero pixel intensity could be represented with a zero, and
all intensities between 1 and 255 could be represented with a nonzero value. A grayscale
image, of course, includes more than two intensity values. Thus, an 8-bit image can be
treated as an input image with 256 pixel intensities. Typically, the image has a single
channel. (For multichanneled input, the operations are performed on each channel
independently.)
Both ilErodeImg and ilDilateImg are derived from ilSpatialImg and thus involve moving
a kernel across an image, but the operation performed is not a computed sum. Instead,
in morphological operations, the kernel is called a ‘‘structuring element’’ (SE) and is

114

Image Processing Operators Provided with IL

represented by an ilKernel. The SE, like the input image, can be interpreted as binary or
grayscale. When applied to an image, a morphological operator returns a quantitative
measure of the image’s geometrical structure in terms of the SE.
The interpretation of the numbers that make up an SE depends on the type of
morphological operation being performed. Negative SE elements are always treated as
logical “do not cares” when the operation is in progress, image pixels under negative SE
elements are ignored. Thus, the support of the SE is limited to those elements that are
nonnegative. This permits the creation of odd-shaped SEs. The image pixel under the
origin is the one potentially modified.
Note: You can change the origin of the SE by using ilKernel’s setOrigin() method. The

default is in the center of the SE.
The result of erosion or dilation on a binary image (regardless of whether the SE is binary
or grayscale) is to turn every pixel either “on” or “off.” A pixel in the output image can
then be assigned one of two intensities, corresponding to whether it is on or off. These
two intensities are typically the maximum and minimum values of the operator image,
which can be set using setMaxValue() and setMinValue() (inherited from ilImage). If
they are not explicitly set, the maximum and minimum values are inherited from the
input image. For the example of an 8-bit image, the minimum value might be 0 and the
maximum 255. A pixel that is 0 in the input image might have a value of 255 in the output
image, and a nonzero input pixel might be 0 in the output.
The interpretation of the image or the SE as binary or grayscale can be controlled through
the enumerated type ilMorphType, as described below.
•

If the input image and the SE are binary (ilMorphType = ilBinBin), the SE is used to
perform a hit-or-miss transformation. That is, if a zero image pixel falls under a zero
SE element, or if a nonzero image pixel falls under a nonzero SE element, the image
pixel beneath the SE origin is turned on (assigned the maximum value) for dilation
and turned off (assigned the minimum value) for erosion. Typically, for binary
images, an SE is composed of negative and positive ones.

•

If the input image is binary and the SE type is grayscale (ilMorphType = ilBinGray),
the nonnegative SE elements determine the support area. In other words, image
pixels under negative SE elements are ignored, but if a positive image pixel falls
under a non-negative SE element, the target pixel (under the SE origin) is turned on
for dilation or off for erosion.

•

If the input image is grayscale and the SE type is binary (ilMorphType = ilGrayBin),
the maximum or minimum (depending on whether dilation or erosion is being

115

Chapter 4: Operating on an Image

performed, respectively) of image pixels falling under positive SE elements is
computed.
•

If the input image and the SE are grayscale and a “set” operation is desired
(ilMorphType = ilGrayGraySet), the maximum or minimum (depending on
whether dilation or erosion is being performed) of image pixels falling under
nonnegative SE elements is computed.

•

If a “function” operation is desired (ilMorphType = ilGrayGrayFct), the
computation is the same as for ilGrayGraySet, except that the SE elements are
added to the image pixels before computing the minimum or maximum.

The constructors for erosion and dilation are shown below:
ilDilateImg(ilImage* inputImage = NULL,
ilMorphType mtype = ilBinGray, ilKernel* se = NULL,
ilEdgeMode eMode = ilPadSrc);
ilErodeImg(ilImage* inputImage = NULL,
ilMorphType mtype = ilBinGray, ilKernel* se = NULL,
ilEdgeMode eMode = ilPadSrc);

Each operator accepts a pointer to an input image (inputImage), a specification of the type
of morphological operation (mtype), a structuring element (the ilKernel pointer se), and an
edge mode (eMode).
The morphological transform types, which are members of the enumerated type
ilMorphType (defined in il/iflDataTypes.h), are summarized below. These types define
whether data in the image and the structuring element (SE) is treated as binary (that is,
having a zero or a nonzero value) or as grayscale (that is, with an appropriate range for
its data type).

116

BinBin

Dilation or erosion on a binary image with a binary SE.

BinGray

Dilation or erosion of a binary image with a grayscale SE. The operation
is performed over the support of nonnegative SE elements.

GrayBin

Dilation or erosion of a grayscale image with a binary SE. The operation
is performed over the positive support of the SE.

GrayGraySet

Dilation or erosion of a grayscale image with a grayscale SE. The
operation is performed over the nonnegative support of the SE.

GrayGrayFct

Dilation or erosion of a grayscale image with a grayscale SE. The dilation
or erosion is performed as a function operation over the nonnegative
support of the SE; that is, the SE elements are added to the image pixels
before the dilation or erosion is performed.

Image Processing Operators Provided with IL

Both ilDilateImg and ilErodeImg define these two functions:
void setMorphType(ilMorphType type);
ilMorphType getMorphType();

setMorphType() allows you to set the type of morphological operation and
getMorphType() returns the type of operation.

Edge Detection
The operators described in this section are gradient operators that produce
edge-enhanced images by performing orthogonal convolutions with particular kernels.
This section focuses on how to use these operators rather than on the specific algorithm
implemented by each of these operators. For more information about the algorithms, see
the reference pages for the specific class.
The classes described in this section inherit directly or indirectly from ilSpatialImg, as
shown in Figure 4-20.

ilConvImg

ilCompassImg

ilLaplaceImg

...

ilOpImg

ilSpatialImg
ilRobertsImg

ilSobelImg

Figure 4-20

Edge Detection Operator Inheritance Hierarchy

The constructors for the ilRobertsImg and ilSobelImg operators take the same
arguments:
ilRobertsImg(ilImage *inputImage= NULL, double biasVal = 0.,
ilEdgeMode edgeMode = ilPadSrc);
ilSobelImg(ilImage *inputImage = NULL, double biasVal = 0.,
ilEdgeMode edgeMode = ilPadSrc);

The image to be transformed is specified by inImg. The other two arguments, which have
default values, indicate a bias value to be added as each pixelwise convolution is

117

Chapter 4: Operating on an Image

performed and how pixels at the edge of a page are to be handled. These arguments have
the same meaning as the ones supplied in the ilConvImg constructor, which is described
in the preceding section. As explained in more detail in the reference pages, these
operators perform two orthogonal, two-dimensional convolutions, which are then
combined with predefined kernels. The resulting images are edge-enhanced images. An
example image produced by ilRobertsImg is shown in Figure 4-21.

Original

Figure 4-21

Filtered

Edge Image Produced by ilRobertsImg

The constructor for the ilLaplaceImg operator uses the same arguments as the
constructors shown above, plus an additional argument that allows you to select one of
two predefined kernels:
ilLaplaceImg(ilImage *inputImage= NULL, double biasVal = 0.,
ilEdgeMode eMode = ilPadSrc, int kerno = 1);

The kerno argument can be either 1 or 2; the corresponding kernels are listed in the
reference page for ilLaplaceImg. You can use setKernel() to specify either kernel after
you have created an ilLaplaceImg object.
A compass operator measures gradients in a specified direction. The ilCompassImg
operator allows you to specify the desired direction as an angle between 0 and 360
degrees or as one of eight compass points. You can also specify the size of the kernel to

118

Image Processing Operators Provided with IL

be used. Once all this information is supplied, a square kernel is generated, which is then
convolved with the image data. Here’s the class constructor:
ilCompassImg(ilImage *inImg= NULL,
float angleDir = ilCompassN, double biasVal = 0.,
int kernSize = 3,ilEdgeMode edgeMode = ilPadSrc);

The angleDir argument can be a number or one of the following values (see Table 4-2),
which correspond to the compass points.
Table 4-2

Compass Directions for the ilCompassImg Operator

Value

Angle (in degrees

ilCompassN

0

ilCompassNE

45

ilCompassE

90

ilCompassSE

135

ilCompassS

180

ilCompassSW

225

ilCompassW

270

ilCompassNW

315

North, or 0 degrees, is the top of an image (as it is displayed using ilDisplay). Angles are
measured from north in a clockwise direction. The bias value and edge mode arguments
for the constructor have the same meaning as those for ilLaplaceImg. Since the kernel is
always square, only one dimension of its size needs to be specified. You can set and
retrieve the bias value with setBias() and getBias(), which are defined by ilOpImg.
Figure 4-22 shows an example image produced by using ilCompassImg.

119

Chapter 4: Operating on an Image

Unfiltered

Figure 4-22

Filtered

A Compass Filtered Image

Once you have created an ilCompassImg operator, you can dynamically change the
direction of the gradient with either setAngle() or setXYWt():
void setAngle(float angleDir = ilCompassN);
void setXYWt(float Xwt = 0.0, float Ywt = 1.);

The setXYWt() function specifies weights in the x and y dimensions, which are then used
to generate the kernel. The ilCompassImg reference page describes in more detail how
the kernel is generated from the angle or weights.
You can query an ilCompassImg about its angle or weights with these functions:
float getAngle();
void getXYWt(float& Xwt, float& Ywt);

Frequency Domain Transformations
it is often convenient to manipulate data in the frequency domain, particularly when
restoring, enhancing, or removing noise from images. The ilRFFTfImg operator
described in this section performs a forward fast Fourier transform (FFT) on an image
(containing “real-valued” data, not complex). Once you have converted an image into
the frequency domain, you can use any of the numerous Fourier operators to manipulate
the image data. Then, when you are finished, you can use ilRFFTiImg, which performs

120

Image Processing Operators Provided with IL

an inverse FFT, to convert back to the spatial domain. Figure 4-23 shows the frequency
domain operators and how they fit into IL inheritance hierarchy.

ilFCrCorrImg
/ilFDyadicImg

...

ilFDivImg
ilFMultImg

ilFPolyadicImg

ilFConjImg
ilFMonadicImg
ilFRaisePwrImg
ilFExpFiltImg
ilFFiltImg
ilFGaussFiltImg
ilFSpecImg
ilFMergeImg

...

ilOpImg
ilRFFTfImg
ilFFTOp
ilRFFTiImg
ilFMagImg
ilFPolarImg
ilFPhaseImg

Figure 4-23

Frequency Domain Operator Inheritance Hierarchy

Forward and Inverse Fourier Transforms

As shown in Figure 4-23, both ilRFFTfImg and ilRFFTiImg inherit publicly from ilOpImg
and privately from ilFFTOp. You should think of these two classes as operators that
simply use the forward and inverse transform functions defined by ilOpImg. ilRFFTiImg
tries to set the page size large enough to hold an entire channel of the image.
The FFTs are performed using the Prime Factor algorithm, using floating point
arithmetic. (For more information on the specifics of this algorithm, see the ilFFTOp
reference page and the article “Symmetric FFTs,” by Paul N. Swarztrauber, Mathematics
of Computation, Vol. 47, Number 175, July 1986, pp. 323-346.) The only restriction this

121

Chapter 4: Operating on an Image

algorithm places on the input image is that it have a real (non-complex) data type other
than iflBit. However, the algorithm is most efficient if the image already contains floating
point data (so it does not have to be converted for processing and then converted back
again), has an iflSeparate order, and has dimensions that are products of small primes.
Dimensions that are a power of two yield the most efficient computation. The reference
pages for each of the Fourier operators described in this section contain more
information about the methods used to perform the computations as well as hints about
how to achieve the greatest possible efficiency.
The constructor for the ilRFFTfImg operator and the member function, ilFFTOp.ilRfft(),
perform a forward FFT.
ilRFFTfImg(ilImage *img = NULL, short option = ilFFTxform2D);
ilStatus ilRfftf(ilImage* src, int srcCh, void* dst,
short opt = ilFFTxform2D, ilMpCacheRequest* req = NULL);

Using the ilRFFTfImg operator to perform a forward FFT is relatively easy. The first
argument is a pointer to the source image that is to be transformed. The second
argument, called option, allows you to choose whether a one- or two-dimensional
transform is performed; if it is:
•

1, a one-dimensional FFT is performed on the rows of data

•

2, a one-dimensional FFT is performed on the columns of data

•

3, a two-dimensional FFT is performed (the default)

You can dynamically change this parameter with the setOption() function.The first four
arguments to ilRFFTfImg() function specify which channel of the source image is to be
transformed and into which channel of the destination image the result should be put. In
this example, channel 0 of srcImg is transformed and placed into channel 0 of destImg. The
size of both of these images must be the same. The last argument for this function
specifies which of the three options described above is desired. (It has the same meaning
as the second argument to the ilRFFTfImg constructor.)
Since the source image must contain real data (not complex numbers), the output is
conjugate-symmetric. In other words, only two of the four quadrants are unique, and
only these are computed for the output. The output is complex, however, so both the real
and imaginary results must be reported. Because of this, the destination image has the

122

Image Processing Operators Provided with IL

same x and y dimensions as the source image. Table 4-3 shows the format of the output
from the ilRFFTfImg operator function. (The origin is in the upper left corner.)
Output of a Forward Fourier Transform (if nx and ny are even)

Table 4-3
0

1

2

3

4

...

nx-3

nx-2

nx-1

0

real

real

imag

real

imag

...

real

imag

real

1

real

real

imag

real

imag

...

real

imag

real

2

imag

real

imag

real

imag

...

real

imag

imag

3

real

real

imag

real

imag

...

real

imag

real

4

imag

real

imag

real

imag

...

real

imag

imag

...

...

...

...

...

...

...

...

...

...

ny-3

real

real

imag

real

imag

...

real

imag

real

ny-2

imag

real

imag

real

imag

...

real

imag

imag

ny-1

real

real

imag

real

imag

...

real

imag

real

Columns 1 through nx-2 contain the real and imaginary components of a complex
transform, for example, column 1 contains the real component and column 2 the
corresponding imaginary component of the first complex FFT output. The column 0
represents the 0-frequency (or DC) component, and column nx-1 represents the highest
(Nyquist) frequency along the x-direction. These two columns resemble the output of a
real-valued FFT. In the example shown, both nx and ny are assumed to be even. If nx were
odd, the Nyquist column would be missing. If ny were odd, the last row shown would
be missing. Table 4-4 shows the output format if both nx and ny are odd.
Output of a Forward Fourier Transform (if nx and ny are odd)

Table 4-4
0

1

2

3

4

0

real

real

imag

real

imag

1

real

real

imag

real

2

imag

real

imag

3

real

real

4

imag

real

...

nx-2

nx-1

...

real

imag

imag

...

real

imag

real

imag

...

real

imag

imag

real

imag

...

real

imag

imag

real

imag

...

real

imag

123

Chapter 4: Operating on an Image

Table 4-4 (continued)

Output of a Forward Fourier Transform (if nx and ny are odd)

0

1

2

3

4

...

...

...

...

...

...

ny-2

real

real

imag

real

ny-1

imag

real

imag

real

...

nx-2

nx-1

...

...

...

imag

...

real

imag

imag

...

real

imag

This format is what is expected as input by all the Fourier operators described in this
section. In particular, the constructor for the ilRFFTiImg operator expects this format in
their source image. They perform an inverse FFT, which is to say they convert the input
Fourier data back to the spatial domain:
ilRFFTfImg(ilImage *img = NULL, short option = ilFFTxform2D);
ilStatus ilRfftf(ilImage* src, int srcCh, void* dst,
short opt = ilFFTxform2D, ilMpCacheRequest* req = NULL);

The ilRFFTiImg constructor takes a pointer to the source image and the same option
argument described above. (The ilRFFTiImg operator also defines the same setOption()
function described above.) For the ilRFFTiImg() function, the source and destination
images (src and dst) must be the same size; the srcCh and dstCh arguments specify the
channel to be transformed and the destination channel number. Both the constructor and
the function produce output data that is real. The output of the forward transform is
multiplied by 1.0/(nx*ny) so that the forward transform followed by the inverse returns
the original image unscaled.
Separating the Magnitude and Phase Components

The operators described in this section allow you to separate the magnitude and phase
components of a complex Fourier image so that you can process or filter them
independently and then combine them into a complete image when you are finished.
Such an operator chain would look like Figure 4-24.

124

Image Processing Operators Provided with IL

ilFMagImg
(operators)

ilFMergeImg

ilRFFTfImg

ilRFFTiImg

(operators)
ilFPhaseImg

Figure 4-24

Magnitude and Phase Fourier Operators

As you might expect from their names, the ilFMagImg operator computes the magnitude
of an input complex Fourier image, and ilFPhaseImg determines the phase component.
The constructors for both of these operators expect the format produced by ilRFFTfImg
(which is described above):
ilFMagImg(ilImage *img = NULL);
ilFPhaseImg(ilImage *img = NULL);

The x -dimension of the output image for both these operators is half of the input image’s
size, plus one; the y dimension is unchanged. The x dimension shrinks because the input
image uses two columns for each Fourier element, one for the real component and one
for the imaginary, whereas the magnitude and phase are not complex. For a complex
number represented by a + ib,
the magnitude is
a2 + b2
and the phase is
atan (b/a)
An operator that is similar to ilFMagImg, ilFSpectImg, computes the spectrum of a
Fourier image. The computation is the same as that performed by ilFMagImg, but all

125

Chapter 4: Operating on an Image

quadrants are represented in the output image, not just the two that are unique. As a
result, the size of the output image is the same as that of the input image, and the origin
of the output image is at its center rather than its upper left corner. You might use an
ilFSpectImg object for displaying, although you probably want to scale the spectral
values using ilHistScaleImg. (This operator is described in “Radiometric
Transformations” on page 136.) An ilFMagImg object is more efficient for processing
since redundant calculations are not performed.
The constructor for ilFSpectImg simply takes a pointer to the source image:
ilFSpectImg(ilImage *img= NULL);

The ilFMergeImg operator merges an ilFMagImg and an ilFPhaseImg to produce the
original whole Fourier image. The merged image is converted from polar to rectangular
form so that it is in the format expected by ilRFFTiImg. The constructor for ilFMergeImg
takes pointers to the two images and an int that specifies the desired x dimension of the
final image:
ilFMergeImg(ilImage *mag, ilImage *ph, int xsize);

The xsize argument is required because the x dimension of a merged image can’t be
uniquely determined from the x dimension of mag or phase. For example, if mag and phase
have x dimensions of 129, the merged image could have an x dimension of either 256 or
257. You can explicitly set the x dimension with setXsize().
Filtering

Two filter operators are provided for use on Fourier images: ilFExpFiltImg and
ilFGaussFiltImg. These operators derive from ilFFiltImg, an abstract class that
implements the basic support for frequency domain filtering. (You can derive your own
filter as described in “Deriving From ilFFiltImg” on page 241.) Both ilFExpFiltImg and
ilFGaussFiltImg expect input in the format produced by ilRFFTfImg. Typically, you’ll
apply the ilRFFTiImg operator to the filtered image in order to view the results in the
spatial domain.
The constructors for these operators are shown below:
ilFExpFiltImg(ilImage *img, float alpha, float beta,
float gamma,float eccent, float theta);
ilFGaussFiltImg(ilImage *img, float hfgain, float dcgain,
float minhalf, float majhalf, float theta);

126

Image Processing Operators Provided with IL

For more information about what these arguments mean, see the filter equations below
and the reference pages for these two operators.
This is the filtering equation used by ilFExpFiltImg:
H ( u, v ) = α + βe

{ γ [ ( a 11 u + a 12 v ) 2 + ( a 21 u + a 22 v ) 2 ]

This is the filtering equation used by ilFGaussFiltImg:
H ( u, v ) = hf + ( dc – hf ) e

– { ( a 11 u + a 12 v ) 2 + ( a 21 u + a 22 v ) 2 }

where for both equations:
H() = transfer function of the filter
u,v = two-dimensional frequency coordinates
σ S cos θ'
σ S sin θ'
σ L sin θ'
σ L cos θ'
a 11 = ------------------- , a 12 = ------------------ , a 21 = – ------------------- , a 22 = -------------------xSize
ySize
xSize
ySize
πθ
θ' = --------- , where θ = angle in degrees of the filter’s orientation
180
xSize = x dimension of the source image
ySize = y dimension of the source image
and where for ilFExpFiltImg:
α = high-frequency asymptote
β = decay coefficient
γ = exponential decay coefficient
1
σs= 1.0 and σ L = -----------------1 – ε2
where ε = eccentricity of equal contours of
the filter and where for ilFGaussFiltImg:
hf = gain of filter at the Nyquist (highest) frequency

127

Chapter 4: Operating on an Image

dc = gain of filter at zero frequency
0.693147
0.693147
------------------------2 and σ L = -----------------------2minHalf
majHalf
minHalf = frequency of half-power point along the minor elliptical axis

σS =

majHalf = frequency of half-power point along the major elliptical axis
Table 4-5 shows two examples of specific values that might be passed in for
ilFGaussFiltImg.
Table 4-5

Sample Parameter Values for ilFGaussFiltImg

Parameter

High-pass

Low-pass

dc

0.004

1.0

hf

3.0

0.002

minHalf

0.01

0.05

majHalf

0.01

0.05

0.0

0.0

The high-pass values create a two-dimensional circular high-pass filter with a cutoff
value of 0.01 on both axes; its DC gain is 0.004, and its gain at the highest frequency is 3.0.
A high-pass filter diminishes the constant or slowly-changing portions of an image and
thereby accentuates the edge portions (creating a high-contrast, edge image). The
low-pass values create a two-dimensional circular low-pass filter with a cutoff value of
0.05 on both axes; its DC gain is 1.0, and its gain at the highest frequency is 0.002. A
low-pass filter diminishes the dramatically changing values at edges in an image and
thereby accentuates the constant or slowly varying portions (creating a blurry image).
See Figure 4-25 and Figure 4-26.

128

Image Processing Operators Provided with IL

Figure 4-25

Original Image

Figure 4-26

Image Processed with ilFGaussFiltImg

Functions are defined in ilFExpFiltImg.h and ilFGaussFiltImg.h to set the value of all the
parameters used in the constructors for both operators.

129

Chapter 4: Operating on an Image

In ilFExpFiltImg.h:
void
void
void
void
void

setAlpha(float val);
setBeta(float val);
setGamma(float val);
setEccent(float val);
setTheta(float val);

In ilFGaussFiltImg.h:
void
void
void
void
void

setHFgain(float val);
setDCgain(float val);
setMinHalf(float val);
setMajHalf(float val);
setTheta(float val);

See the reference pages for more information about these functions.
Single-input Operators

The two operators described in this section are ilFConjImg and ilFRaisePwrImg, both of
which derive from ilFMonadicImg. (See “Deriving From ilFMonadicImg or
ilFDyadicImg” on page 238 for more information about deriving your own operator
from this class.) ilFConjImg and ilFRaisePwrImg expect a source image in the format
produced by ilRFFTfImg. Typically, you’ll need to convert ilFRaisePwrImg’s output to
the spatial domain by using ilRFFTiImg. (You do not typically need to convert the result
of applying ilFConjImg to an image back to the spatial domain; usually, it is used in the
middle of a chain of operators in the frequency domain.)
As its name suggests, ilFConjImg computes the complex conjugate of an image; it also
multiplies the complex values by a real factor:
ilFConjImg(ilImage *img=NULL, float scale = 1.0);

The scale argument is used to multiply or scale the values; the default value of 1.0 results
in no scaling. You can change the scaling factor with setScale(). ilFConjImg is useful in
computing the magnitude squared of the Fourier transform. For example, assume theImg
is a pointer to a valid ilImage in the spatial domain:
ilRFFTfImg forwardImg(theImg);
ilFConjImg conjugateImg(&forwardImg);
ilFMultImg magSquaredImg(&forwardImg, &conjugateImg);

You can then display magSquaredImg.

130

Image Processing Operators Provided with IL

The ilFRaisePwrImg operator raises the natural log of the magnitude values of a Fourier
image by a power, exponentiates the result, and writes the values back in complex
rectangular form:
e ( ln m ) p where m

= magnitude and p = specified power

This root-filtering operation is useful for image sharpening. The constructor for this class
is shown below:
ilFRaisePwrImg(ilImage* src, float power);

The log of the magnitude values of the source image, src, are raised by power,
exponentiated, and converted back to complex rectangular form. The valid range for
power is 0.0-1.0. You can set this value dynamically with setPower().
Dual-input Operators

Three operators take two Fourier images as inputs:
•

ilFCrCorrImg, which computes the cross-correlation of two images

•

ilFMultImg, which multiplies two images

•

ilFDivImg, which divides two images

These classes derive from ilFDyadicImg, which implements the basic support for
dual-input Fourier operators, and they expect input images in the format produced by
ilRFFTfImg. To convert the processed data back to the spatial domain, you need to apply
the inverse transform implemented by ilRFFTiImg. See “Deriving From ilMonadicImg or
ilPolyadicImg” on page 228 for more information about deriving your own dual-input
Fourier operator.
The constructors for ilFCrCorrImg, ilFMultImg, and ilFDivImg expect two images,
which must be the same size:
ilFCrCorrImg(ilImage *img1 = NULL, ilImage *img2 = NULL);
ilFMultImg(ilImage *img1 = NULL, ilImage *img2 = NULL);
ilFDivImg(ilImage *img1 = NULL, ilImage *img2 = NULL,
int ckDiv = 1);

To compute the cross-correlation, ilFCrCorrImg multiplies src1 by the conjugate of src2
and then normalizes the result using the DC (or (0,0)) coefficient of src1. One of the
principal applications of cross-correlation in image processing is in prototype matching,
where one tries to match a given unknown image to a known image. The closest match

131

Chapter 4: Operating on an Image

can be found by selecting the image that yields the correlation function with the largest
value.
Multiplying two Fourier images is equivalent to convolving them in the spatial domain.
Since the Fourier algorithm is very efficient, you might want to choose ilFMultImg over
one of ilConvImg’s subclasses if you are using a large kernel for the convolution.
ilFDivImg divides src1 by src2 and, by default, checks for division by zero according to
the following rules:
•

If the numerator of the real or imaginary part is positive and the denominator is
zero, the result is the largest possible floating point value (3.40282346e+38).

•

If the numerator of the real or imaginary part is negative and the denominator is
zero, the result is the smallest possible floating point value (-3.40282346e+38).

•

If both the numerator and the denominator are zero, the result is zero.

You can call setCheck() and pass in a 0 to prevent ilFDivImg from checking for division
by zero.
You can use ilFDivImg in image restoration. Given the Fourier transform of a degraded
or noisy image and the Fourier transform of the noise function (or “noise image”), you
can retrieve a clean image by dividing (in the frequency domain) the degraded image by
the noise image. Once converted back to the spatial domain, you can then display the
clean image.

Generation of Statistical Data
it is often desirable to collect statistical information about an image, such as how
frequently various pixel values occur and what the minimum and maximum pixel values
are. The ilImgStat class computes this kind of information for an entire image or for a
specified region within an image. More specifically, for each channel of image data, it
computes:
•

a one-dimensional histogram showing frequency of pixel values

•

the minimum and maximum pixel values

•

the mean and standard deviation of the data, calculated from the histogram

The ilImgStat class inherits from ilLink, as shown in Figure 4-27.

132

Image Processing Operators Provided with IL

ilLink

Figure 4-27

ilImgStat

The ilImgStat Inheritance

ilImgStat does not derive from ilImage, so its constructor does not create an ilImage. It
derives from ilLink. Thus, an ilImgStat object cannot be passed as an image to another
operator, but it might be one of an operator’s input arguments.
Multiprocessing on an ilImgStat object can turned on or off and queried using the
enableMP() and isMPenabled() functions.
ilImgStat has two parents: the input image from which the data is derived, and the ROI,
if any. If either of these objects is altered, the two ilLink inheritance mechanism ensures
that the ilImgStat object reconfigures itself.
Calculation of the statistics is deferred until it is triggered by one of the get...() methods.
If only the minimum-maximum is requested, a histogram is not computed. For any other
get...() method, the histogram is computed. If the data order is separate, on the minimal
subset of the channels is analyzed. The analysis results (minimum, maximum, historicity,
mean, standard deviation, are cached in the ilImgStat to avoid recalculation. If the object
is altered either directly or indirectly with inheritance, the cached data id discarded.
Note: There is an “auto calc” flag that modifies this default behavior. For more

information, see the ilImgStat man page.
The constructor for the ilImgStat class allows you to specify whether the data should be
computed for the entire source image or for just a portion of it, as shown in the next code
fragment. The portion is defined as a region of interest (ROI); see “Defining a Region of
Interest” on page 153 for more information about the ilRoi class, which defines an ROI
within an image.
ilImgStat(ilImage* img=NULL, ilRoi* roi=NULL,
int xoffset=0, int yoffset=0, int zoffset=-1, nz=0);

The xoffset, yoffset, and zoffset parameters represent the offsets into the input image, img,
at which the ROI is placed. The z argument specifies the starting z value, and nz indicates
the size of the z tile. Thus, you can use these values to effectively create a 3-D ROI. The
coordinates of the roi image are specified in the coordinate space of the input image.
You can also specify an ilRoi and its offsets for the ilImgStat with the following setRoi()
functions:

133

Chapter 4: Operating on an Image

ilImgStat::setROI(ilRoi* roi, int xoffset=0, int yoffset=0);
ilImgStat::setZ(int z, int nz=1);

If no ROI is specified, ilImgStat performs its computations over the whole image.
ilImgStat::clearZ() unsets the tile in the Z dimension.
An Image’s Histogram

An image’s histogram, which is computed for each channel of image data, is defined by:
•

the starting and ending pixel values—these establish the endpoints of the
histogram’s range.

•

the number of bins—the range is evenly divided into a specified number of bins.

•

the size of each bin—the size is the range covered by each bin; this is computed by
dividing the total range by the number of bins.

You use the following methods to determine the number of bins, the bin size, and the
lower limit of the first bin for any particular channel:
double lowerLimit = myImgStat.getStart(1);
double upperLimit = myImgStat.getEnd(1);
double binSize = myImgStat.getBinSize(1);
int binCount = myImgStat.getBinCount(1)

The argument for these functions is an int that specifies the desired channel.
Once you have created an ilImgStat object, you can specify the limits of a histogram using
the following methods:
void setInput(ilImage* in)
void setLimits(double start, double end);
void setBinCount(int count);

where in is the image associated with the histogram, start and end specify the lower and
upper endpoints of the range of pixel values, and count is the number of bins in which
pixel values are collected. The maximum number of bins allowed is 4096.
To access the histogram of the source image’s pixel values, use the getHist() function:
unsigned long* getHist(int chan=0);

where chan is the color channel.

134

Image Processing Operators Provided with IL

The getHist() function returns a pointer to an array that is allocated by ilImgStat. The
values in the array correspond to the number of pixels that have values within each bin’s
respective range. To represent a probability distribution, copy the long array into a float
array, and then divide each element of the array by the total number of pixels used to
compute the histogram for that particular channel. You can obtain the number of pixels
used with getTotal():
long totalPixelCount = myImgStat.getTotal(1);

The argument for this function is an int that specifies the desired channel. (The number
of pixels used for each of the channels might vary if you have specified different
endpoints for the different channels.)
If the image’s pixel ordering is iflSeparate, you can make multiple calls to getHist() for
each channel and specify varying numbers of bins and starting points and endpoints.
However, the histograms for all channels of iflInterleaved or iflSequential images are
computed on the first call to getHist(), so the number of bins and the starting points and
endpoints are fixed for subsequent calls. If you need to change the histogram’s attributes
for subsequent calls, use reset(). This function deallocates the array created with
getHist() and enables you to start over. (In general, you should call reset() or the
ilImgStat destructor as soon as you are finished with a histogram to minimize memory
usage.) If you need a histogram you have already computed, copy it into your own buffer
before calling reset().
Minimum, Maximum, Mean, and Standard deviation

The ilImgStat class defines functions that return the minimum value, maximum value,
mean, and standard deviation of a particular channel:
double
double
double
double

getMin(int c=0);
getMax(int c=0);
getMean(int c=0);
getStdDev(int c=0);

These functions all return the desired number as a double, regardless of the data type of
the image.
Other Functions

Two other support functions are provided:
void setHwEnable(ilHwAccelEnable enable);
ilHwAccelEnable getHwEnable();

135

Chapter 4: Operating on an Image

void qCalcStats(ilMpNode* parent, ilMpManager** pMgr=NULL);

You can use the first function shown above to enable and disable hardware acceleration
by passing in TRUE or FALSE, respectively. You can use getHwEnable() to determine
whether or not acceleration is enabled.
The last function allows your application to calculate a histogram asynchronously.

Radiometric Transformations
This section describes a set of operators that adjust all the pixels of an image so that
together they have certain specified characteristics. Three of the operators described in
this section—ilHistNormImg, ilHistEqImg, and ilHistScaleImg—modify an image’s
pixel values channel by channel, so that the image’s histogram has certain desired
properties. You can limit the area for which statistics are computed by specifying an ROI
and its offsets when you create these operators; the operators then adjust all the pixels of
the image so that the entire image’s histogram matches that computed for the ROI. (See
“Defining a Region of Interest” on page 153 for more information about ROIs.) If you
have already created an image’s histogram using ilImgStat as described in the previous
section, you can pass a pointer to the existing ilImgStat object to speed the
transformations performed by these operators.
The following radiometric operators are described in this section:

136

ilScaleImg

linearly scales the pixel data of an image so that it falls in a
new specified range

ilHistNormImg

transforms an image so that its histogram is normalized
(Gaussian) and so that it has a specified mean and standard
deviation

ilHistEqImg

transforms an image so that its pixel values are uniformly
distributed (so that the cumulative histogram is linear)

ilHistScaleImg

clamps values to a specified percentage distribution of the
high- and low-intensity pixels and scales the remaining data
between the clamp values

ilThreshImg

sets each pixel to the image’s minimum or maximum value,
depending on whether the pixel is less than or greater than a
specified threshold value

ilLutImg

transforms a source image using a specified lookup table

Image Processing Operators Provided with IL

ilPiecewiseImg

transforms a source image using a lookup table created with
a piecewise linear mapping function

The operators that perform radiometric scaling, ilScaleImg and ilHistScaleImg, are
accelerated on certain hardware platforms. The ilLutImg operator and the operators
derived from it, such as ilPiecewiseImg, ilHistNormImg and ilHistEqImg, are also
accelerated provided they meet the constraints specified in “Using Hardware
Acceleration” on page 253. The ilThreshImg operator is also accelerated through the LUT
mechanism, even though it is not derived from ilLutImg. All these classes derive directly
or indirectly from ilMonadicImg, as shown in Figure 4-28.

ilScaleImg

...

ilOpImg

ilMonadicImg

ilHistScaleImg

ilThreshImg

ilPiecewiseImg
ilLutImg

ilHistNormImg
ilArithLutImg

ilHistLutImg
ilHistEqImg

Figure 4-28

Radiometric Operator Inheritance Hierarchy

Scaling an Image

The ilScaleImg operator linearly scales the pixel data of an image so that it falls in a
specified range. If you do not know the range of the input pixels, the first constructor
shown below must be used. This constructor uses the minimum and maximum value
fields of the input image to determine the input range, and it assumes an output range of
0 to 255. If you want to override the range of the input pixel data, you can use the second
constructor and also specify an output range. The default is 0 to 255.
ilScaleImg(ilImage* img = NULL);
ilScaleImg(ilImage* img, double inMin, double inMax,
double outMin=0, double outMax=255.999);

137

Chapter 4: Operating on an Image

Pixels of value inMin are scaled to outMin, while those of value inMax are scaled to
outMax. Pixels channel values lying between these extremes are scaled accordingly.
Pixels outside the input domain are clamped between outMin and outMax.
The scaling function is normally computed based on inMin and inMax (the domain) and
outMin and outMax (the range). To do this scaling, ilScaleImg computes the slope and
intercept of a linear function of the form:
f ( x ) = ( x ⋅ slope ) + intercept
Thus, an input pixel of value x becomes an output pixel of value f(x). The slope and
intercept are computed as follows:
( outMax – outMin )
slope = -----------------------------------------------------( inMax – inMin )
intercept = outMin – ( slope ⋅ inMin )
You can alter the operator’s parameters with these member functions:
void setRange(double outMin, double outMax);
void setDomain(double inMin, double inMax);

You can control the scaling behavior with these functions:
void
void
void
void

resetDomain();
resetRange();
resetScaling();
setScaling(double slope, double intercept);

resetDomain() invalidates the current input levels and, if none are specified using
setDomain(), the minimum and maximum values of the input images are used for the
domain.
resetRange() invalidates the current output levels and, if none are specified using
setRange(), default values are computed using the input domain and the scaling values
(slope and intercept). An example image produced using ilScaleImg is shown in
Figure 4-29.
resetScaling() forces the operator to forget any values explicitly set for slope and intercept
and to compute them as shown above.
setScaling() allows you to explicitly set the values of the slope and intercept of the scaling
function.

138

Image Processing Operators Provided with IL

Figure 4-29

Using Scaling

Histogram Operators

Both ilHistNormImg and ilHistEqImg derive from ilHistLutImg, which itself derives
from ilArithLutImg. This inheritance allows the histogram operators to use lookup tables
to determine resulting values, rather than perform the computations on a per-pixel basis.
As a result, the histogram operators are more efficient.
The constructors for ilHistNormImg are:
ilHistNormImg(ilImage *img, iflPixel &mn, iflPixel &std,
ilImgStat *imgstat = NULL, ilRoi *Roi = NULL,
int xoffset = 0, int yoffset = 0, int zoffset = 0);
ilHistNormImg(ilImage *img=NULL, ilImgStat *imgstat=NULL,
ilRoi *Roi=NULL, int xoffset=0, int yoffset=0,
int zoffset=0);

139

Chapter 4: Operating on an Image

The first constructor allows you to specify the source image and the desired mean, mn.
and standard deviation, std. The second constructor takes a source image and computes
default values for the mean and standard deviation. The mean for each channel is
computed as the average of the minimum and maximum values of the source image for
that channel. The standard deviation is set to 1.0 for each channel.
The iflPixels can use any data type, but their number of channels must match that of the
source image. If you have already created an ilImgStat object (for the source or even a
different image), you can pass a pointer to it. This makes ilHistNormImg more efficient.
If you supply both an ilImgStat and an ilRoi, the histogram computed for the ilImgStat
is used and the ilRoi is ignored.
You can dynamically change the mean, the standard deviation, the ilImgStat object, and
the ilRoi and its offsets with the following ilHistNormImg.h functions:
void
void
void
void

setMean(iflPixel& mean);
setStdev(iflPixel& stdev);
setImgStat(ilImgStat* imgstat);
setRoi(ilRoi* Roi, int xoffset = 0, int yoffset = 0);

The setImgStat() and setRoi() functions are inherited from ilHistLutImg.
Histogram equalization and histogram scaling of an image are often performed to
enhance the contrast of an image. Histogram equalization results in an image with pixel
values that are more evenly distributed.
The constructor for ilHistEqImg is shown below:
ilHistEqImg(ilImage *img = NULL, ilImgStat *imgstat = NULL,
ilRoi *Roi = NULL, int xoffset=0, int yoffset=0,
int zoffset=0);

As shown, you specify the source image, the ilImgStat object if one exists, and an optional
ROI along with its offsets. This class also inherits setImgStat() and setRoi() functions as
does ilHistNormImg.
The constructor for ilHistScaleImg is more complicated:
ilHistScaleImg(ilImage* img = NULL, double lowClip=0,
double highClip=0, double outMin=0, double outMax=255,
ilImgStat* imgstat=NULL, ilRoi* Roi=NULL,
int xoffset = 0, int yoffset = 0);

140

Image Processing Operators Provided with IL

The src argument specifies the source image. The next four arguments specify how the
source image should be transformed. The highClip and lowClip arguments indicate what
percentage of the high and low intensity pixels should be clamped to the values specified
by outMax and outMin, respectively. Imagine that the pixels are sorted in order of
increasing intensity, as in a histogram. Then, highClip percent of the highest-intensity
pixels are set to the outMax value, and lowClip percent of the lowest-intensity pixels are
set to the outMin value. After the desired pixels have been clipped, the remaining pixels
are scaled linearly between the clamp values. The optional ilImgStat and ilRoi objects
(and offsets) each have the same meaning as with ilHistNormImg.
You can dynamically change all these arguments with the following ilHistScaleImg
functions:
void
void
void
void

setImgStat(ilImgStat* imgstat);
setRoi(ilRoi* Roi, int xoffset = 0, int yoffset = 0);
setClip(double lowClip, double highClip);
setRange(double outMin, double outMax);

One other useful function, setHistLimits(), allows you to change the limits between
which the histogram is to be computed:
void setHistLimits(double low, double high);

The two arguments, low and high, define the histogram’s limits.
Be careful when changing the input to any of the histogram operators by using
setInput(). (See “Dynamically Reconfiguring a Chain” on page 57 for more information.)
If an ilImgStat has already been specified in a histogram operator constructor and then
setInput() is called, the old ilImgStat is used unless you call setImgStat() with a new one.
You can use NULL in setImgStat() to force a new one to be computed.
The Threshold Operator

The ilThreshImg operator sets each pixel (on a channel by channel basis) to the image’s
minimum or maximum allowable value, depending on whether the pixel is less than or
greater than a specified threshold value. (See “Minimum and Maximum Pixel Values” on
page 30 for more information about how to set an image’s minimum and maximum pixel
values.)
To create an ilThreshImg operator, you can use one of the following constructors:
ilThreshImg(ilImage* img, const iflPixel& thresh);
ilThreshImg(ilImage* img= NULL, float val = 0);

141

Chapter 4: Operating on an Image

In the first constructor, the threshold is specified as an iflPixel, and a different threshold
level can be applied to each channel of the source image. In the second constructor, the
same threshold, val, is applied to all channels.
Each channel or each pixel of the source image is compared to the threshold value, thresh
or val. If the channel value is less than the threshold value, it is set to the image’s
minimum channel value. If the channel value is greater than or equal to the threshold
value, it is set to the maximum channel value. (If thresh is a single-channel pixel, its value
is used for all channels of the source image.)
You can query an image about its threshold value and dynamically change this value
with these functions:
void getThresh(iflPixel& thresh);
void setThresh(float val);
void setThresh(const iflPixel& thresh);

getThresh() returns the threshold value by reference, and setThresh() sets the threshold
value.
ilLutImg

The ilLutImg class transforms a source image using a specified lookup table (LUT). As
mentioned previously, ilArithLutImg (see “Single-input Operators” on page 91) and
ilHistLutImg (see “Histogram Operators” on page 139) derive from ilLutImg. Normally,
the LUT and the image have the same number of channels. However, two other
possibilities are allowed: if the LUT has only one channel, it is applied to each channel of
the image. If the source image has only one channel while the LUT has n channels, each
LUT channel is applied to the source image in turn, producing an ilLutImg with n
channels. (For any other combination, the ilStatus value ilLUTSIZEMISMATCH is
returned by any data access operations.)
The first constructor below allows you to specify the source image and the LUT. The
second one lets you specify the source image and sets the LUT to NULL. You can later
specify a LUT using the setLookUpTable() function.
ilLutImg(ilImage* src, const iflLut& table);
ilLutImg(ilImage* src = NULL);

See “Using iflLut” on page 360 for more information about the iflLut class and also for
an explanation of how lookup tables can be stored and retrieved using SGI image files.

142

Image Processing Operators Provided with IL

You can dynamically change or retrieve the LUT with these functions:
ilStatus setLookUpTable(const iflLut& table);

If you change the LUT, the output number of channels and data type are updated, if
necessary, to accommodate the new LUT.
ilPiecewiseImg

The ilPiecewiseImg class, derived from ilLutImg, simplifies the task of constructing a
lookup table when only a piecewise linear mapping is needed from the input pixels to
the output data. The constructor accepts the source image, a list of breakpoints, and the
length of that list:
ilPiecewiseImg(ilImage* inputImage = NULL,
const iflXYSfloat* bkpts=NULL, int length=0);

A breakpoint is a point on a piecewise continuous function where two continuous
segments meet, as shown in Figure 4-30.

Output
channel
values

Breakpoint
Endpoint

0

Figure 4-30

Input channel values

255

Breakpoints along a Piecewise Continuous Function

The endpoints, 0 and 255, are made breakpoints by default (this does not affect the length
of the breakpoints list). If a breakpoint is entered outside the range, it is clamped to the
appropriate endpoint.
Several functions are provided to manipulate the breakpoints:
ilStatus setBreakpoints(const iflXYSfloat* bkpts=NULL,
int length=0, int chan=-1);
ilStatus insertPoint(const iflXYSfloat& point, int index,
int chan=-1);

143

Chapter 4: Operating on an Image

ilStatus replacePoint(const iflXYSfloat& point, int index,
int chan=-1);
ilStatus removePoint(int index, int chan=-1);

144

setBreakpoints()

allows you to specify a new list of breakpoints (of length). You
can specify a list for a specific channel with the chan argument;
if this is minus 1 (the default), the list is used for all channels in
the image.

insertPoint()

inserts a breakpoint point after the one at index in the list for
channel chan.

replacePoint()

replaces the breakpoint at index in the breakpoint list for
channel chan with point.

removePoint()

removes the breakpoint at index; you specify which channel’s
breakpoint list with chan.

Image Processing Operators Provided with IL

You can query an ilPiecewiseImg about its breakpoints with these functions:
int getBreakpoints(iflXYSfloat* bkpts, int chan=0);
int getNumBreakpoints(int chan=0);
void getPoint(iflXYSfloat& point, int index, int chan=0);
float findPoint(iflXYSfloat& loc, int& index,
int forInsert=0, int chan=0);

getBreakpoints()

accepts a pointer to a list of breakpoints and returns the
length of the breakpoint list for chan as an int and the
breakpoint list itself through bkpts (you must allocate
enough space in bkpts before this function call).

getNumBreakpoints()

returns the number of breakpoints in the breakpoint list
for chan.

getPoint()

returns the breakpoint at index in the breakpoint list for
chan by reference in point.

findPoint()

accepts a location (loc), an index into the breakpoints list for
chan, and a flag specifying whether the closest breakpoint
should be found (forInsert = 0) or whether the closest edge
should be found (forInsert = 1). In either case, the distance
between the given location and the found location is
returned as a float, the breakpoint is returned by reference
in loc, and the index of that breakpoint is returned in index.

In all of the above functions, chan is 0 by default, specifying the first channel of the image.
Figure 4-31 shows an example of an application with a graphical user interface (imgview)
that can be written with ilPiecewiseImg.

145

Chapter 4: Operating on an Image

Original

Figure 4-31

LUT Editor RGB Interface

Edited

Using a Lookup Table Editor to Set Breakpoints

Combining Images
The three operators described in this section—ilBlendImg, ilMergeImg, and
ilCombineImg—combine two or more images into one using different methods:
•

ilBlendImg blends two images using a specified alpha value or alpha images that
indicate how to weight the images relative to each other.

•

ilMergeImg merges a series of images into a single multiple-channel image.

•

ilCombineImg combines two images using a mask to define which portions of the
two images to use in the final combined image.

These three classes have very different pedigrees, as shown in Figure 4-32.

ilMergeImg

ilImage

ilCacheImg

ilOpImg

ilPolyadicImg

ilBlendImg

ilCombineImg

Figure 4-32

146

ilBlendImg, ilMergeImg, and ilCombineImg Inheritance Hierarchy

Image Processing Operators Provided with IL

ilBlendImg

The constructors for ilBlendImg allow you to specify a constant alpha value or to specify
third and fourth images that contain alpha values for each pixel of the foreground and
background images. You can also select the way in which the foreground and
background images are blended:
ilBlendImg(ilImage* fore, ilImage* bkgd, float alpha);
ilBlendImg(ilImage* fore = NULL, ilImage* bkgd = NULL,
ilImage* alphaf = NULL, ilImage* alphab=NULL,
ilCompose comp=ilAplusB);

The first constructor specifies one constant alpha value (which should fall between 0.0
and 1.0) that is used to calculate a foreground and background alpha. If the second
constructor is used, the alpha values are taken from the first channel of alphaf (for the
foreground alphas) and alphab (for the background alphas). The other channels, if any, are
ignored. In the default mode (ilAplusB), if alphab is NULL, then the background alpha
values for each pixel are computed from alphaf as 1 - alphaf. Figure 4-33 shows an example
image produced using the ilBlendImg operator and the ilAplusB compose mode.
The second constructor also allows you to specify the composition mode. See Figure 4-34
for an explanation of these modes. The default is ilAplusB. The composition modes are
defined in the header file il/iflDataTypes.h.

147

Chapter 4: Operating on an Image

Original 1

Original Mask

Original 2

Figure 4-33

Blend of 1 and 2

Blended Images

The foreground, background, and alpha images must all be the same size. The alpha
values defined by alphaf and alphab are normalized to the range (0-1), based on the
minimum and maximum allowable pixel values of alphaf and alphab. The foreground and
background alphas are calculated as follows:
foreα = alpha , backα = 1 - alpha or
foreα = alphaf, backα = alphab (if alphab is not NULL), and
backα = 1 - alphaf (if alphab is NULL)

148

Image Processing Operators Provided with IL

The blending function, which is used for each pixel, is:
F A ⋅ foreground ⋅ foreα + F B ⋅ background ⋅ backα
The composition mode determines FA and FB. For the default composition mode
(ilAplusB), they are both equal to 1.0, see Figure 4-34.
If ilImgA is the foreground image and ilImgB is the background image, then
αA = alphaf and αB= alphab .
However, when alphaB=NULL, then
αB = alphaf
You may set the composition method with setBlendMode(). It takes one argument of
type ilCompose:
void setBlendMode(ilCompose mode = ilAplusB);

You can explicitly set the minimum and maximum allowable pixel values of the alpha
images alphaf and alphab using these functions:
void setAlphaRange(float fmin, float fmax);
void setAlphaRange(float fmin, float fmax,
float bmin, float bmax);

The first function sets the normalizing values for the foreground alpha. The second sets
the minimum and maximum values of the alpha for the foreground and background
images.
To query an ilBlendImg about its normalizing values, use:
void getAlphaRange(float& fmin, float& fmax);
void getAlphaRange(float& fmin, float& fmax,
float& bmin, float& bmax);

The first function returns the normalizing values for the foreground alpha, and the
second function returns the normalizing values for both the foreground and background
alphas. You can also dynamically change the alpha images or the constant alpha value:
ilStatus setAlphaPlane(ilImage* alphaImg);
ilStatus setAlphaPlane(ilImage* alphaf, ilImage* alphab)
ilStatus setConstAlpha(float val);

149

Chapter 4: Operating on an Image

The first function shown above sets the foreground alpha image, while the second
function sets both the foreground and background alpha images. The third function sets
the constant alpha value. You can also use setOffset() (inherited from ilPolyadicImg) to
offset the foreground image with respect to the background image.
FA

FB

ilImgA

1

0

ilImgB

0

1

ilAoverB

1

1−αΑ

ilBoverA

1−α

1

ilAinB

αΒ

0

ilBinA

0

αΑ

ilAoutB

1−αΒ

0

ilBoutA

0

1−αΑ

ilAatopB

αΒ

1−αΑ

ilBatopA

1−αΒ

αΑ

ilAxorB

1−αΒ

1−αΑ

ilAplusB

1

1

Mode

Figure 4-34

150

Diagram

Β

Composition Modes for ilBlendImg

Image Processing Operators Provided with IL

ilMergeImg

An ilMergeImg consists of a single ilImage formed by merging a number of images. The
number of channels of the merged image equals the sum of the number of channels in all
the individual input images. All the input images should be the same size, but you can
assign a different data type or order to the final ilMergeImg as it is created:
ilMergeImg(ilImage** imgPtr, int nimg,
iflOrder order=iflInterleaved,
iflDataType dtype=iflDataType(0));
ilMergeImg(int nimg, ilImage** imgPtr);

In both of these constructors, imgPtr is an array of pointers to the ordered input ilImages.
The first nimg ilImages in the array are merged and the rest are ignored. (imgPtr should
have at least nimg pointers.) The first constructor lets you specify an order and data type
for the merged image. If the default data type numilTypes is used, the data type of the
merged image is the largest data type of the ilImages. If the second constructor is used,
the order and data type of the merged image are the same as those of the first ilImage
pointed to in the imgPtr array.
ilCombineImg

An ilCombineImg takes two ilImages of the same size and uses an ROI (and its offsets)
to determine which pixels to use in the final image (pixels that are inside the ROI are
taken from the foreground image, and pixels that are outside the ROI are taken from the
background image):
ilCombineImg(ilImage* fore=NULL, ilImage* bkgd=NULL,
ilRoi* roi=NULL,int xoffset=0, int yoffset=0,
int zoffset=0);

See “Defining a Region of Interest” on page 153 for more information about creating an
ilRoi object. The xoffset and yoffset parameters specify the offsets at which the ROI is
placed in the foreground and background images; they are specified in the coordinate
space of the fore image. You can change the ROI and its offsets after the combined image
is created, and you can obtain a pointer to it with these ilHistScaleImg functions:
void setRoi(ilRoi* roi, int xoffset = 0, int yoffset = 0,
zoffset = 0);
ilRoi* getRoi();

151

Chapter 4: Operating on an Image

Constant-valued Images
The ilConstImg class allows you to create an object that returns a constant value
whenever its data is read. You might use this class as an input to one of the operators
described in the “Dual-input Operators” on page 94, for example, to multiply each pixel
in an image by a constant value. Remember that ilConstImg is not an operator since it
derives directly from ilImage.
The ilConstImg class defines only one function, its constructor:
ilConstImg(const iflPixel& fill);

The specified iflPixel is the value returned whenever the image’s data is read, regardless
of how much data is read. Since an ilConstImg stores only one iflPixel, it uses much less
memory than, for example, an ilMemoryImg filled with pixels. To change an
ilConstImg’s pixel value after you have created an ilConstImg object, use the setFill()
function defined in ilImage and described in “Fill Value” on page 29.

Using a Null Operator
As its name suggests, the ilNopImg operator performs no operation at all. It is useful for
caching the results defined by a non-cached class, such as ilMemoryImg (described in
“Importing and Exporting Image Data” on page 78). it is also useful if you just want to
change some of the attributes of any image (for example, data type, data ordering, or
page size) and need to cache the result. Note that this class is a real operator, as it derives
from ilMonadicImg.
The ilNopImg class defines one public member function, its constructor:
ilNopImg(ilImage* inputImage = NULL);

An image stored as an ilMemoryImg cannot take advantage of IL’s on-demand paging
mechanism, since it does not derive from ilCacheImg. ilNopImg, however, is derived
(indirectly) from ilCacheImg. Thus, storing that ilMemoryImg as an ilNopImg allows
you to page that image.

152

Defining a Region of Interest

Defining a Region of Interest
Some IL programs, especially those that deal with large images, may need to apply an
operator to only a portion of an entire image. When this is the case, you can restrict the
processing area to a region of interest (ROI). An ROI allows you to modify irregular
regions of an image. IL provides two principal classes that let you restrict the data that
can be accessed:
•

ilRoiImg, which associates a ROI with an image so that subsequent operations on
the image affect only the data inside the ROI

•

ilSubImg, which allows a rectangular portion of an image to be treated as if it were
an independent image

In some situations, these two classes might appear to have similar effects, but they
actually achieve their results through very different means, and they have different uses.
ilRoiImg, derived from ilCombineImg, is the same size as the initial image. The
difference is that portions of the ilRoiImg are “masked out”—set to a specified
background value—so that they will not be affected by processing. You use an ilRoiImg
when you wish to modify a portion of an image while leaving the rest of the image intact.
This is the traditional masking, or ROI, concept.
ilSubImg, derived from ilOpImg, does not actually hold any data itself; it merely
implements the standard data access ilImage functions—getSubTile3D(),
setSubTile3D(), and copyTileCfg()—so that they access only the data in the subimage.
When you call one of the access functions, you specify the origin and size of the desired
tile in the subimage. The ilSubImg maps the coordinates of the desired tile to the source
image so that the correct data is accessed. An ilSubImg can be used as a rectangular ROI,
but it is most useful for manipulating the input images to an operator to achieve
particular results. For example, you can use an ilSubImg to offset two images relative to
each other before they are fed into an ilAddImg operator to be added together. (You can
also do this with the ilPolyadicImg.setOffset() function in ilPolyadicImg.) Or you can
select the red and blue channels of an image using two ilSubImgs and then add them
together.
Once you have created either an ilSubImg or an ilRoiImg, you can use it in an operator
chain just as you would any other ilImage. You can also write data back into ilSubImg or
ilRoiImg, which you cannot do with an operator (since all operators are read-only).
When you do write data back into an ilSubImg or an ilRoiImg, the input image is
modified appropriately. The next sections describe how to use these two classes.

153

Chapter 4: Operating on an Image

Creating an ilRoiImg
Typically, you use an ilRoiImg when you are displaying processed data or writing it to a
file. By restricting the area that needs to be processed, you can prevent data from being
processed unnecessarily.
Before you can create an ilRoiImg, you need to create the following:
•

the source ilImage that is to be masked with the ROI

•

the actual ROI itself, in the form of an ilRoi object

•

the x and y offsets for placing the ROI into the source image

•

the background value, an iflPixel, that is used to fill areas outside the ROI

The source image can be any ilImage, and it can be part of an already existing operator
chain. The background value defines the ilImage’s values outside the ROI. As shown
below, the constructor for the ilRoiImg class takes pointers to all three of these objects:
ilRoiImg(ilImage *Img, ilRoi *Roi, iflPixel &bkgd,
int xoffset=0, int yoffset=0);

This constructor associates the ilRoi with the source ilImage and sets the ilRoiImg’s
background value. The xoffset and yoffset values determine where the ROI is placed; they
are specified in the src image’s coordinate space. Subsequent operations to the ilRoiImg
affect only the image data inside the specified ilRoi. Any attribute of an ilRoiImg that is
not explicitly set is inherited from its source image.
Once an ilRoiImg is created, you can modify its associated ilRoi or the background value
by calling ilHistScaleImg.setRoi() or ilRoiImg.setBkgd(). These ilHistScaleImg
functions take a pointer to the desired ilRoi or iflPixel:
void setRoi(ilRoi* roi, int xoffset = 0, int yoffset = 0);
void setBkgd(iflPixel& bkgd);

You can also query an ilRoiImg about its ROI or background value:
ilRoi* getRoi();
void getBkgd(iflPixel& bkgd);

The ilRoi base class defines the basic concept of a region of interest in IL. It is an abstract
class, so you must use one of the classes that derive from it to create an ROI. (You can also
derive your own class to define an ROI that more specifically matches your needs. See
“Deriving From ilRoi” on page 242 to learn more about deriving from ilRoi.) An ilRoi is

154

Defining a Region of Interest

a two-dimensional object with its own x and y dimensions and its own coordinate space.
If you imagine the ilRoi placed on top of the image and yourself viewing it from above,
you would see regions of the image inside and outside the ilRoi. The regions inside are
considered valid and are accessible for image processing operations; those outside the
ilRoi are invalid and are typically set to a background value for processing. The same
ilRoi can be associated with different images (which can be different sizes), and it can be
placed at different offsets within each image. This functionality is achieved through the
ilRoiMap class, which is described later. You manage the ilRoi’s coordinate space with
ilRoi.setOrientation() and ilRoi.getOrientation().
Currently, IL provides two classes derived from ilRoi, as shown in Figure 4-35.

ilRectRoi
ilLink

ilRoi
ilImgRoi

Figure 4-35

ilBitMapRoi

lRoi’s Subclasses

An ilRectRoi defines a rectangular ROI, and an ilBitMapRoi defines a bitmap of any
shape that can be used as an ROI.
A Rectangular ROI

As its name suggests, ilRectRoi allows you to define a rectangular ROI:
ilRectRoi myRoi(20, 30, 1);

All the arguments for the ilRectRoi constructor are of type int. The first two specify the
sizes in the x and y dimensions (20 and 30) of the rectangle to be used as the ROI. The
optional last argument, which can be either 1 or 0, indicates whether the area inside or
outside the rectangle should be considered the valid area. The default value is 1, which
defines the inside of the rectangle as the valid area. You specify the image that the
ilRectRoi is associated with and the offsets into the image later so that the same ilRectRoi
can be used for different images at different offsets. In addition, operators that take an
ROI as an input also take the offsets as arguments.

155

Chapter 4: Operating on an Image

You can also determine which is the valid area (the inside or the outside of the rectangle)
and change the current designation:
int getValidValue();
ilStatus setValidValue(int val);

The first function returns either a 1 or a 0 to indicate that the inside or the outside is valid,
and the second function sets the valid area.

Creating an ilSubImg
The ilSubImg class defines three constructors that let you create a subimage that is a
different size from the source image. The first constructor is for two-dimensional images,
the second for three-dimensional images, the third for four-dimensional images, and the
last for use as a NULL constructor.
ilSubImg(ilImage *src, int xs, int ys, int xsz, int ysz,
ilConfig* config = NULL);
ilSubImg(ilImage *src, int xs, int ys, int zs, int xsz,
int ysz, int zsz, ilConfig* config = NULL);
ilSubImg(ilImage *src, ilConfig *config);
ilSubImg();

The first argument in all of these functions is a pointer to the source image. The next
arguments specify the location of the origin of the subimage (xs, ys, zs, and cs), measured
in pixels in the source image, and the dimensions of the subimage (xsz, ysz, zsz, and csz),
as shown in Figure 4-36. (This figure assumes that the subimage’s coordinate space is
iflLowerLeftOrigin.) If the dimensions are larger than the source image, the subimage is
padded with the source image’s fill value.
Source Image
Subimage
ysz
(xs, ys)
xsz

Figure 4-36

156

Source Image and Subimage

Defining a Region of Interest

The last, optional argument for these constructors is a pointer to an ilConfig object that
specifies the configuration of the subimage. If this argument is not supplied, the
subimage inherits its configuration from the source image.
Another constructor is provided for convenience when the subimage has the same size
as the source image but a different configuration:
ilSubImg(ilImage* src, ilConfig* config);

You can use the ilConfig argument for any of these constructors to select a subset of the
source image’s channels and to reorder them; you can also use it to set the coordinate
space, data type, and pixel ordering of the subimage.
Once you have created an ilSubImg, you can modify several of its attributes—size, data
type, order, color model, and coordinate space—using the functions defined in ilImage.
To change an ilSubImg’s configuration after you have created it, use setConfig(). This
function takes a pointer to an ilConfig and modifies the ilSubImg accordingly. Any
attribute of an ilSubImg that is not explicitly set is inherited from its source image.
You can also translate the origin of a subimage after it is been created:
const int xorigin = 20;
const int yorigin = 20;
mySubImg.setMouse(xorigin, yorigin);

As shown, setMouse() expects const int arguments. For a three-dimensional image,
supply a third argument for the z dimension. For a four-dimensional image, supply a
fourth c dimension. The ilSubImg’s origin, not its size, is affected by setMouse(), as
shown in Figure 4-37.
Translated
Subimage
Image
Subimage

Figure 4-37

Translated Subimage

157

Chapter 4: Operating on an Image

You can also query a subimage about its origin using one of the following methods:
ilDisplay.getMouse(int& x, int& y);
ilDisplay.getMouse(int& x, int& y,
iflOrientation orientation);

As shown, the overloaded getMouse() retrieves the origin by reference.
The virtual method, ilImage.hasPages(), indicates whether a class implements paging
and is defined by ilSubImg. It returns TRUE if its parent implements paging and FALSE
otherwise.

158

Chapter 5

5. Displaying an Image

This chapter describes how to display and manage a set of images on the screen using
IL’s display facility. As part of this facility, numerous functions are provided to help you
develop an interactive image-processing program. You can use these functions to move
images, perform wipes, roam around an image, and create split views of multiple
images.
This chapter describes IL’s display facility in the following major sections:
•

“Overview of the Display Facility” on page 160 describes the sequence of
operations you must perform to display an image.

•

“A Simple Interactive Display Program” on page 165 lists and describes a program
that opens an image file, performs an operation on it, and allows interactive
viewing of both images.

•

“Creating an ilDisplay” on page 169 describes in detail how to open a window and
create an ilDisplay.

•

“View and Display Basics” on page 171 describes basic concepts such as setting
background color and page borders and deferring drawing of a view.

•

“Managing Views” on page 177 describes how to manage the view stack and how
to retrieve information from views.

•

“Applying a Display Operator” on page 184 tells you how to use display operators
to draw views, relocate or resize them, and update them.

•

“A More Complicated Interactive Display Program” on page 196 contains a
program illustrating control of a display.

159

Chapter 5: Displaying an Image

Overview of the Display Facility
IL display classes described in this chapter are shown shaded in Figure 5-1.

ilImage
ilLink

ilView

ilDisplay

ilStereoView

ilViewer

ilGLXConfig

Figure 5-1

IL Display Classes

With IL’s display facility, you can display any combination of IL or X images in an X or
OpenGL window. These images can be positioned anywhere within the window and can
overlap each other.
Overlapped regions are displayed based on a stacking order such that the image on top
is visible, as shown in Figure 5-2.

160

Overview of the Display Facility

X Window

Img1

Img2

Img3

Img4

Figure 5-2

Assuming:
addView(Img1)
addView(Img2)
addView(Img3)
addView(Img4)
ilViews

Background
(display area)

Stacked Images in an X Window

In order to assemble such a display, you must follow these steps:
1.

Create or open the images.
You can display any combination of ilImage, ilXImage, or XImage using either
OpenGL or X to render them. (An XImage is an X Window struct.) Often, displayed
images are the product of an image processing chain. For example, you might want
to display the original unprocessed image, an intermediate stage of the chain, and
the final image. In some cases, you might want to display only a portion of an
image.

2. Configure and open a window.
To open an X window, use the standard X calls. To open a OpenGL window, use the
OpenGL calls explained in “A Sample Program in C++” on page 2.
3. Create a display.
Use calls to ilDisplay functions.
4. Add the images to the display.
Use calls to ilDisplay functions.
5. Cause the images to be displayed.
Use calls to ilView and ilDisplay functions.

161

Chapter 5: Displaying an Image

Note: You should assume that any function discussed in this chapter is an IL function,

unless it is explicitly identified as a OpenGL or X function.
The three principal classes within the IL display facility are
•

ilDisplay—Manages one or more ilViews in an X or OpenGL window. The entire
window is used for display. An ilDisplay object maintains a stack of ilViews and
provides functions to manipulate them. ilViewer derives from ilDisplay, which
manages the display of images in an X window with X event handling.

•

ilView—Maps an ilImage or XImage to a region within the ilDisplay. It has various
attributes such as view position, view size, image position, border color, and border
width.

•

ilFrameBufferImg—Acts as a base class for images that reside in the frame buffer.
There is one derived classes: ilXWindowImg.

When an ilDisplay is created, it creates an ilXWindowImg for X rendering. The display
image is configured to occupy the entire window specified by the application. As
Figure 5-3 illustrates, the creation of an ilDisplay object defines a display area in which
views are drawn.
Memory
X Window

ilDisplay
object
Display Area

Figure 5-3

ilDisplay Object Creates a Display Area

When you want to add an image to ilDisplay, create an ilView to control where you want
the image to be displayed. This view is pushed onto an indexed view stack and a pointer
to it is returned to your application. As you add more images, you must create an ilView
for each image. These views are pushed onto the view stack. When a view is added to the
stack, it is pushed onto the top by default. However, you can specify a particular index
to control where the view is put in the view stack. An ilView has various attributes, such
as

162

Overview of the Display Facility

•

view position, which controls where in the window the image is displayed

•

view size, which controls how much of the image is displayed

•

image position, which controls what part of the image is displayed

The position of an ilView in the view stack controls its visibility on the screen, as shown
in Figure 5-2. The view on the top of the stack is fully visible. A view at the bottom of the
stack is obscured by the views above it.
In Figure 5-4, two ilView objects have been created and the positions of the
corresponding views in the display defined. Two images to be displayed have been
added to the view stack.
Memory
ilView
objects
view stack

Img2

addView(Img2)

view2
Img1
view1

addView(Img1)
top

Figure 5-4

ilView Objects Map Images to Display Regions

When ilDisplay draws its contents, the position and size of each ilView, as well as the
stacking order, are used to determine what portion of each view is visible.
ilFrameBufferImg is called to render the images into the frame buffer. Each image is
converted to the proper data type, order, color model, and coordinate space as necessary
for displaying. Figure 5-5 shows the display after the views have been drawn.

163

Chapter 5: Displaying an Image

Memory
Img2
view stack
Display(view1)
Img1

Display(view2)
view2
top
view1

Figure 5-5

Display Area After Views Are Drawn

In addition to the views added by an application, ilDisplay creates a background view.
This background view is the size of the window and is always at the bottom of the view
stack. You cannot control it other than to change its color from the default, which is black.
ilDisplay provides several operators to manipulate ilViews as well as functions to
facilitate interactive display. The display operators enable you to move a view, change its
size, or move the image within the view. The display operators are discussed in detail in
“Applying a Display Operator” on page 184. By default, view manipulation also causes
the display to be redrawn; however, a sequence of display operations can be performed
with drawing deferred. In addition, an application can explicitly control drawing.
ilDisplay is optimized to draw only the areas that have changed or that have been
exposed.

Scrolling Windows
You can change the size of the image by reducing its display size. By making the image
size smaller than the display, you, in effect, create a scrollable window. To set and read
the visible area in which an image can display, you use the setVisibleArea() and
getVisibleArea() functions in ilDisplay, defined as follows:
void setVisibleArea(int x = 0, int y = 0, int nx = 0,
int ny = 0);
void getVisibleArea(int& x, int& y, int& nx, int& ny);

The arguments define the lower left and upper right coordinates of the visible display
area.

164

A Simple Interactive Display Program

A Simple Interactive Display Program
Now look at a simple interactive program that shows the IL display facility in action.
This program opens an image file and applies a threshold operator to it. Both the original
image and the processed image are displayed in a window, stacked on top of each other.
You can wipe the original image away gradually so that you can see the processed image
beneath it. Wiping changes the view size. The best way to understand wiping, of course,
is to compile and run the display program. It is available online in
/usr/share/src/il/guide/displayEx.c++
When you run the program, you see a window displaying the original image. The
processed image is actually underneath the original one, but you cannot see it, since the
images are opaque and of the same size. If you click in the window with the left mouse
button, a red highlight border appears around the image, indicating that it is ready to be
wiped. To wipe, click the left mouse button near any edge of the image and drag toward
the center of the image. As you drag, the processed image becomes visible as the original
image is wiped away; release the button to stop the wiping. You can wipe any edge or
corner of the original image. To exit the program, use the normal window manager menu
command.

Sample Program Code
The code for the sample program is shown in Example 5-1 and discussed in the
paragraphs following that. All the ilDisplay functions used in the program are explained
in more detail in the appropriate sections in this chapter.
Example 5-1
#include
#include
#include
#include
#include

A Simple Interactive Display Program







const int Border = 10;
operation

// Threshold in pixels for edge finding

void
main (int argc, char* argv[])

165

Chapter 5: Displaying an Image

{
if (argc < 2) {
printf (“Usage: %s in-image1\n”, argv[0]);
exit(0);
}
// Step 1: Open an image file and create a threshold image.
ilFileImg in(argv[1]);
if (in.getStatus() != ilOKAY) {
char buf[400];
fprintf(stderr, “Could not open image file %s: %s\n”,
argv[1], ilStatusToString(in.getStatus(), buf,
sizeof(buf)));
exit(0);
}
// Step 2: Create threshold image, open an X window, and create
// ilDisplay object.
float threshVal = 100.;
iflPixel threshPix(iflFloat, 1, &threshVal);
ilThreshImg thresh(&in, threshPix);
iflSize size;
in.getSize(size);
Display* dpy = XOpenDisplay(NULL);
ilDisplay disp(dpy, size.x, size.y);
disp.addView(&thresh);
ilView* inView = disp.addView(&in);
disp.setBorders(TRUE);
// Step 3: Process the image, allowing to wipe between original and
// processed images using the left mouse button.
int active = TRUE;
int wipemode = 0;
Window win = disp.getWindow();
while (active) {
iflXYint winSize;
XEvent event;
XNextEvent(dpy, &event);
switch (event.type) {

166

A Simple Interactive Display Program

case MotionNotify:
// flush the event queue
Window rw, cw;
int rx, ry, x, y;
unsigned int state;
XQueryPointer(dpy, win, &rw, &cw, &rx, &ry, &x,
&y, &state);
if (event.xmotion.state&Button1Mask)
inView->wipe(x, y, wipemode|ilClip);
else
wipemode = inView->findEdge(x, y, Border);
break;
case ButtonPress:
disp.setMouse(event.xbutton.x, event.xbutton.y);
break;
case Expose:
disp.display(NULL, ilDefer|ilCenter);
disp.redraw();
disp.getSize(winSize.x, winSize.y);
break;
case DestroyNotify:
disp.destroyNotify();
active = FALSE;
break;
}
}
XCloseDisplay(dpy);
}

Sample Program Comments
The first half of this program should be familiar to you; it is very similar to the sample
program in Chapter 1, “Writing an ImageVision Library Program.” The first several lines
of code include the necessary header files. If the user specifies fewer than two arguments
(the name of the program and the name of the image file), the program prints an error
message and then exits.

167

Chapter 5: Displaying an Image

Step 1: Open an Input Image File and Create a Threshold Image

The specified image file is opened as an ilFileImg object called in. Next, an iflPixel object
is created for use by the threshold operator; the threshold value chosen for this example
is 100.0. The ilThreshImg operator thresh sets each pixel to its maximum possible value if
the pixel value is greater than or equal to the threshold value, or to its minimum possible
value if it is less than the threshold value.
Step 2: Open an X Window and Create an ilDisplay Object

After the threshold image is created, establish a connection to the X server with the X
function XOpenDisplay().
Next, you create an ilDisplay object by passing in the appropriate X window and display
IDs. As shown, the processed image thresh is added first, and then the original image, in,
is added using addView(). The addView() function creates an ilView for the specified
image, adds it to the display’s view stack, and returns a pointer to the ilView. The pointer
to the ilView associated with in is used later in the program.
The order in which images are added determines their stacking order when they are
displayed; the last view added is on top of the others. In this case, the original image is
displayed on top of (and completely obscuring) the processed image. You can reorder the
views as needed.
The setBorders() function is used with default arguments in this example to highlight all
views in the view stack. You can also specify the border color and width.
Step 3: Process Events

Processing events is a critical task for interactive programs. All of the previously
identified inputs (events) must be handled. The event loop in this example processes
events continuously while the program is active.
The code in the event loop uses the following variables:

168

active

Indicates that the window is still active. It becomes FALSE when the user
selects “Quit” from the window menu.

event

Holds the event read from the X event queue with XNextEvent().

winSize

Indicates the current size of the window.

Creating an ilDisplay

wipemode

Indicates which edge of the image to wipe.

x and y

Hold the x and y positions of the mouse, respectively, as the user drags
to perform a wipe.

The code in the event loop takes the following actions in response to user actions:
•

MotionNotify. As the mouse is moved, the x and y values are accessed with the X
function XQueryPointer(). The current xy location of the mouse is passed to wipe(),
which changes the size of the view by moving one or more edges. While the mouse
button is pressed, motion causes a wipe to be performed. If the mouse button is not
pressed, then findEdge() is called. The findEdge() function returns the edge(s) near
the xy location specified and is saved in wipemode. (In this program, the user must
click within 10 pixels of the edge.) When wipe() is called, wipemode specifies which
edges to wipe.

•

Button Press. When the user presses the left mouse button to start wiping, the
setMouse() function is called to save the current x and y mouse positions.

•

Expose. The first time through the loop, an Expose event is processed, causing the
entire display to be drawn on the screen with the redraw() function.

•

DestroyNotify. When the user quits, the active flag is set to FALSE.

Creating an ilDisplay
To incorporate IL’s display facility in your program, you must follow these steps:
1.

Open and configure a window.

2. Create an ilDisplay object to manage the window.
3. Add and manipulate images you want to display.
4. Apply the desired display operator(s) to one or more of the views.
This section discusses the first two of these items. The remaining two items are covered
in detail in following sections.

Opening an X Window and Creating an ilDisplay Object
Before creating an ilDisplay object, you must open a window. To do this, use the standard
X call XOpenDisplay(),. as follows:

169

Chapter 5: Displaying an Image

iflSize size;
in.getSize(size);
Display* dpy = XOpenDisplay(NULL);

An ilDisplay object manages views of images within the window passed to it. If an X
window is passed, render mode specifies whether X or OpenGL should be used to render
the images. The constructor for ilDisplay is shown below:
ilDisplay(Display* display, int width, int height,
int attr=ilVisDoubleBuffer, int minComponentSize=8,
int mode = ilDefault,
long eventMask = ExposureMask | KeyPressMask |
PointerMotionMask | PointerMotionHintMask |
ButtonPressMask | ButtonReleaseMask |
StructureNotifyMask);

The following statement creates an ilDisplay object. It takes as its first argument dpy, the
X connection created with XOpenDisplay(). The other arguments define the size of the
window created by ilDisplay:
ilDisplay disp(dpy, size.x, size.y);

The in.getSize() function returns the size of the window to create, and the values of size.x
and size.y define the X and Y dimensions of the window accordingly.
When an ilDisplay object is created, the display origin is the lower left corner. The entire
window is used for drawing, but this window may be a subwindow within an
application.
Note: The ilDisplay class uses many enumerated types, which are listed in “Enumerated
Types and Constants” on page 369 and the header file il/ilDisplayDefs.h.

Adding a View to the ilDisplay Object
Once you have an ilDisplay object bound to an X window, use the ilDisplay member
functions addView() and setBorders() to display the image, as shown in the following
code:
ilView* inView = disp.addView(&in);
disp.setBorders(TRUE);

170

Choosing OpenGL or X Rendering

The &in value is the input image that is ready for manipulation and display. The
setBorders() function enables the display of the default border, ilDefault, around the
view.

Deallocating the Display
After the user has finished with the image, you deallocate the ilView object, ilDisplay
object, and the X window by calling XCloseDisplay(displayName), where displayName
is the name of the ilDisplay object.

Choosing OpenGL or X Rendering
The ImageVision library supports OpenGL as well as X rendering. IL version 3.1 allows
you to choose between the two with the following ilConfigure methods:
ilStatus ilSetRenderingStyle(Display* dpy, ilRenderingStyle style)
ilRenderingStyle ilGetRenderingStyle(Display* dpy)

where ilRenderingStyle is one of the following enumerations: ilOpenGLRendering or
ilXRendering. By default, ilOpenGLRendering is chosen if available. ilXRendering is
used primarily for rendering to a remote machine that does not support OpenGL.
You use ilSetRenderingStyle() to request a rendering style explicitly. If
ilOpenGLRendering is requested but not supported on the X connection, a status of
ilUNSUPPORTED is returned, and ilXRendering is set.
ilGetRenderingStyle() returns the current style.

View and Display Basics
Once you have created a display object, the next step is to add views to this display and
then apply display operators to these views. Before learning more about views, however,
you need to be familiar with some basic concepts that apply to both displays and the
views contained within them.

171

Chapter 5: Displaying an Image

Background Color
If the images being displayed do not cover the entire display area, the ilDisplay’s
background view is seen in the uncovered areas. The background may also be revealed
if images are dragged around or resized by the user. By default, an ilDisplay uses black
as the background color. You can set the color to any pixel value with setBackground():
myDisp.setBackground(float 0, float 1, float 0);

The three arguments correspond to red, green, and blue, respectively. Each color value is
between 0 and 1, inclusive.
In the previous line of code, the background color is set to green.
You can also retrieve an ilDisplay’s current background color:
myDisp.getBackground(float& red, float& green, float& blue);

The returned values for the references are between 0 and 1, inclusive.

Borders
All ilViews have borders, but by default they are not drawn (that is, they are turned off).
You can use ilView’s setBorders() to turn borders on (TRUE) or off (FALSE):
void setBorders(int flag);

When borders are turned off, the highlight flag (see “Finding a View” on page 181) is also
turned off. The borders are painted or erased immediately unless painting is deferred.
Note that borders are painted inside the view.
In addition, both the borders and the NOP flag can be controlled using the select
functions on ilView (see “Preventing View Operations” on page 174 to learn more about
the nop flag). When select() is called, borders are turned on and its nop flag is turned off.
When unselect() is called, borders are turned off and its nop flag is turned on. The
isSelected() function returns TRUE if the view is selected, or FALSE otherwise:
void select();
void unselect ();
int isSelected();

172

View and Display Basics

You can also specify the width and color of the borders:
void setBorderWidth(unsigned int bordWidth);
void setBorderColor(float red, float green, float blue);

The first function sets the width of the border in pixels to bordWidth; by default, a border
has a width of two pixels. (bordWidth should be a number greater than or equal to zero.)
The second function sets the color of the border to the specified colors, each with a value
between 0 and 1, inclusive.
For convenience, you may set border parameters on all the views in an ilDisplay’s view
stack by calling the corresponding functions on ilDisplay. (You can exclude particular
views in the stack from being acted upon by these functions by setting a nop flag in each
view you wish to exclude. See “Deferring Drawing” on page 174.) For example:
myDisp.setBorders(TRUE);
myDisp.setBorderWidth(5);
myDisp.setBorderColor(0, 1, 0);

There are no convenience functions for getBorders(), getBorderWidth(), or
getBorderColor() in the ilDisplay class since the information may vary from view to
view.
Border Styles

You can set and read the style of the border using the setBorderStyle() and
setBorderStyle() functions in ilView, defined as follows:
void setBorderStyle(int style = ilViewBdrSolidLines)
{ bStyle = style; setState(ilViewBorders); }
int getBorderStyle() { return bStyle; }

The possible border styles are defined by the following enum:
enum ilViewBorderStyle {
ilViewBdrSolidLines
ilViewBdrDashedLines
ilViewBdrCornerHandles
ilViewBdrMiddleHandles

=
=
=
=

0,
1,
2,
3

//
//
//
//

Solid lines (old style)
Dashed lines
Handles on corners
Handles on mid-side

173

Chapter 5: Displaying an Image

Preventing View Operations
To keep any view in the stack from being operated upon, use the setNop() function to set
the nop flag:
void ilDisplay::setNop(int nop, ilView* view);
void ilView::setNop(int nop);

If the nop argument is TRUE, then the view will not be operated on. To allow operations
to take place on a view, nop should be FALSE. You can use the function isNop() to
determine the state of the nop flag:
int ilDisplay::isNop(ilView* view);
int ilView::isNop();

If you need to perform an operation on each view in the stack regardless of the value of
each view’s nop flag, pass the ilDop flag in the mode for that operation.
If an operation is called on a view, the nop flag is overridden. For example, the statement
below ignores the nop flag on the specified view:
view->wipe();

Deferring Drawing
Drawing can be deferred by calling setDefer() on ilDisplay or ilView. When used to defer
the display, nothing is drawn; however, each view can be individually deferred as well.
These calls are shown below:
void ilDisplay::setDefer(int def, ilView *view=NULL);
void ilView::setDefer(int def);

In the ilDisplay version, you specify the view in which you wish to defer drawing (the
default is all views) by setting the ilView pointer argument to
•

NULL, which causes all views in the view stack to be affected

•

a pointer to an ilView in the view stack

You might want to defer drawing until you have made a series of changes to an
ilDisplay’s attributes (or to those of its views) so that they all take effect simultaneously.
You might also want to defer drawing while you apply more than one display operator
to avoid drawing intermediate results. In addition, most of the display operators allow

174

View and Display Basics

you to pass the ilDefer flag (see “Mode Flags” on page 175) to defer drawing. (Display
operators are described in more detail in “Applying a Display Operator” on page 184.)
To defer drawing, call setDefer() and pass TRUE as its def argument. After that, the
display is not redrawn until you call setDefer() with FALSE as its def argument. You can
check whether drawing is deferred with isDefer():
int ilDisplay::isDefer(ilView *view=NULL);
int ilView::isDefer();

This function returns TRUE or FALSE to indicate whether drawing has been deferred or
not.

The Drawing Area
An ilDisplay assumes that it can draw anywhere in the window that is been passed to it.
You can retrieve the current size of the drawing area with getSize(), which returns the x
and y dimensions by reference:
void getSize(int& x, int& y);

Managing the Cache
With global cache management, using ilView to manage the cache on its input is
unnecessary. The various cache management methods on ilView have no effect. For more
information, refer to “The Cache” on page 32 and “Optimizing Use of Cache” on
page 247 for a discussion of the global cache management scheme.

Mode Flags
All the display operators use a mode argument to control the display of views. This mode
is a bitwise-OR’d combination of flags that control the operator. You can use the ilDisplay
member functions, setMode() and clrMode() to set and clear the mode flags.

175

Chapter 5: Displaying an Image

The flags are defined as enumerated values (see il/ilDisplayDefs.h). Some flag types are
described below.
Display Flags

Display flags specify various display modes. Examples are:
•

ilClip to clip an image to the edge of the display or view

•

ilDefer to defer painting

•

ilDop to override the nop flag

Coordinate Flags

Coordinate flags specify how the resizing, relocating, and update operators are to
interpret coordinate values. Examples are:
•

ilDelVal where x,y is interpreted as delta relative to the current values

•

ilRelVal to interpret the x,y coordinates relative to the starting x,y (starting x,y is
updated)

•

ilAbsVal to interpret the x,y coordinates as absolute values

•

ilOldRel to interpret the x,y coordinates relative to the starting x,y (starting x,y is not
updated)

Wipe Mode Flags

Wipe mode flags specify the edges in a wipe operation. Some examples are:
•

ilTopEdge to do the wipe from the top edge

•

ilLeftEdge to do the wipe from the left edge

Align Mode Flags

Align mode flags specify image alignment. Some examples are:

176

•

ilTopLeft to align the view from the top left corner

•

ilCenter to align the view to the center of the window or the image to the center of
the view

Managing Views

The sample program shown at the beginning of this chapter contains an example of the
use of the mode argument. In this example, the display operator initializes all views in
the view stack, aligns the views to the center of the image, and defers the painting of the
view until later.
disp.display(NULL, ilDefer|ilCenter);

Managing Views
Once an ilDisplay has been created, you can create views of the images you want
displayed. As views are created, they are pushed onto the view stack. You can also
retrieve views from the stack, replace the images within the views with other images,
remove views, and reorder the views in the stack. This section explains how to perform
these tasks.
Note: If an error occurs while rendering part of a view, the offending tile is painted with

the error color (see getErrorColor() or setErrorColor()), and the status is set on ilDisplay.
The error color defaults to magenta, but can be set per view with setErrorColor().
The error color functions are defined in ilView, as follows:
void getErrorColor(float& red, float& green, float& blue)
{ err.get(red, green, blue); }
void setErrorColor(float red, float green, float blue)
{ err.set(red, green, blue); }

The values of the colors are between 0 and 1, inclusive.

Adding Images
The addView() function creates an ilView and adds it to the view stack. The image is
drawn when addView() is called, unless ilDefer is passed in mode. It returns a pointer to
the ilView for the ilImage (or XImage) pointer passed in
ilView* addView(ilImage* img, int index, int mode);
ilView* addView(ilImage* img, int mode=ilCenter);
ilView* addView(XImage* img, int index, int mode);
ilView* addView(XImage* img, int mode=ilCenter);

177

Chapter 5: Displaying an Image

You can call addView() with just the image or the image and the display mode. In this
case, the view index defaults to 0 (top of the stack). If you use the version of addView()
that takes an index, you can specify the location in the view stack where the image is to
go.
The mode parameter controls the creation and position of the ilView. By default, the view
is centered, not clipped to the display window, and is painted after being added.
However, this behavior can be modified using various display mode flags such as ilClip
and ilDefer. See “Mode Flags” on page 175 and the ilDisplay reference page for more
details.
If an image has a z dimension that is greater than one, you can choose which xy plane of
the image to display. By default, the first plane (z = 0) is displayed. To display a different
plane, call setZ() on ilView:
void setZ(int startZ);

The startZ argument specifies the desired plane of the image in the view that the function
is called on. ilView’s getZ() function takes no arguments and returns the current z plane
being displayed of the corresponding image.

Stereo Viewing
If your machine is capable of stereo and stereo is supported by IL on that machine, you
can turn on stereo viewing mode. A stereo view can be created as shown below:
ilStereoView(ilDisplay* disply, ilImage* LImg,
ilImage* RImg, int mode = 0);
ilStereoView(ilDisplay* disply, ilImage* img, int zLeft = 0,
int zRight = 1, int mode = 0);

Using the first version of the constructor, pointers to the left and right images are passed
to this method on ilDisplay. The last argument specifies the display mode for the view.
Currently, only OpenGL render mode is supported. This constructor requires that you set
up an IL chain for the left and right images.
The second version of the constructor takes a single image with the left and right images
stored in the z dimension. The parameters zLeft and zRight specify the index in the z
dimension corresponding to the left and right images. The benefit of this approach is that
you can use a single IL chain to process both images.

178

Managing Views

With either constructor, the relative screen positions of the left and right images can be
adjusted (see the ilStereoView reference page). If the hardware does not support stereo,
or if stereo mode is disabled, only the left image is displayed. Also note that IL can
display a mixture of monoscopic and stereoscopic views in the same stereo window.
To review an example of a stereo view application, see /usr/share/src/il/ilstereoview.c++.

Retrieving Views
You can obtain a pointer to any view in the stack with getView(). There are two versions
of this function, one that takes an index and another that takes a pointer to an ilImage:
ilView* getView(int index = 0);
ilView* getView(ilImage* img);

Both functions return a pointer to the corresponding ilView. If the image appears in more
than one view, the view that is nearest the top of the stack is returned.
You can also retrieve the index corresponding to a particular view:
int theIndx = myDisp.getViewIndex(someView);
int theIndx = myDisp.getViewIndex(someImg);

The getViewIndex() function takes a pointer to an ilView (someView) or a pointer to an
ilImage (someImg) and returns its index as an int (theIndx).
To determine how many views are in the view stack, call getNumViews().

Retrieving Images
You can obtain a pointer to the image in a particular view with getImg() or getXImg():
ilImage* myImg = someView->getImg();
XImage* myXImg = someOtherView->getXImg();

A pointer to the ilImage or XImage in the view is returned. (Here, someView and
someOtherView are ilView pointers.)
To obtain pointers to the images in a stereo view, use getLImg() and getRImg():
ilImage* myLeft = someStereoView->getLImg();
ilImage* myRight = someStereoView->getRImg();

179

Chapter 5: Displaying an Image

A pointer to the left ilImage is returned from getLImg() and a pointer to the right from
getRImg(). (Here, someStereoView is an ilStereoView pointer.)

Removing Views
You can remove a view from the stack by deleting the view or by calling deleteView() on
ilDisplay. This function removes the specified view from the stack and deletes it:
void deleteView(ilView* view);

Replacing Images
An ilView object allows you to replace its image:
void setImg(ilImage* ilInImg);
void setXImg(XImage* xInImg);

The argument is a pointer to the image you want the view to hold. This image replaces
the image mapped to the view.

Reordering the View Stack
Several functions are provided by ilDisplay to reorder the view stack. The push()
function pushes the specified view down count places in the stack. By default, it pushes
the view to the bottom. Similarly, the pop() function pops the specified view up count
places in the stack. By default, it pops the view to the top. On both push and pop, when
count is 1, the view is moved one position in the view stack. In addition, the swap()
function swaps two views in the stack. These functions are shown below:
push(ilView *view, int count=0);
pop(ilView *view, int count=0);
swap(ilView *view1, ilView *view2);

180

Managing Views

Finding a View
Sometimes you need to find the view at a specified location. In an interactive program,
the mouse is typically used to select a view. To find the view at a given x,y location,
findView() can be called on ilDisplay, as shown below:
ilView* findView(int x, int y, int mode = ilDspCoord);

This function returns a pointer to the topmost ilView found at location xy within the
display. If there is no view at xy, it returns NULL. If ilHighlight is passed in mode, the view
is highlighted if found. When a view is highlighted, its borders are turned on. However,
only one view at a time can be highlighted. If ilDspCoord is passed in mode (the default),
the xy coordinates are interpreted relative to the origin of the display area (display
coordinates). If ilScrCoord in passed in mode, then the xy coordinates are interpreted
relative to the screen (screen coordinates). Recall that the origin of the display area
coincides with that of the window.

Finding an Edge
You may need an edge of a view for certain operations. Sometimes, you want to
determine which edge of a view the cursor is near. This is especially useful for wiping,
as described in “Applying a Display Operator” on page 184. For this, use ilView’s
findEdge() function:
int findEdge(int x, int y, int margin = -1,
int mode = ilDspCoord);

This function determines which edge of the view is nearest to the specified xy
coordinates. If the specified point is within margin pixels from an edge, that edge is
returned. By default, the margin is either the default margin (15) or the current border
width, whichever is greater. The mode argument can be either ilDspCoord (the default)
or ilScrCoord to indicate whether x and y are specified in display or screen coordinates.
The value returned by findEdge() is a bitwise-OR’d combination of the following values:
ilNoView

The coordinates lie outside margin pixels of all views.

ilRightEdge

The coordinates are within margin from the right edge.

ilLeftEdge

The coordinates are within margin from the left edge.

ilTopEdge

The coordinates are within margin from the top edge.

ilBottomEdge

The coordinates are within margin from the bottom edge.

181

Chapter 5: Displaying an Image

ilAllEdge

The coordinates are within margin from all edges; this is an unusual case
since it implies that margin is very large relative to the image. The
ilAllEdge value is used primarily as an argument for wipe(), which is
described in “Applying a Display Operator” on page 184.

ilNoEdge

The coordinates do not lie within margin from any edge.

If a combination of two intersecting edges is returned—for example,
ilRightEdge|ilTopEdge—you can treat the value as corresponding to a corner, in this
case the upper right corner. Note that you can also receive a value such as ilTopRight,
which is equivalent to ilTopEdge|ilRightEdge.
ilDisplay also defines a findEdge() function, which finds the edge on all views. For each
view, it saves the edge for later use with wipeSplit().

Operating on a Pixel
You can obtain the actual pixel data at a specified point in a view with getPixel() (defined
by both ilDisplay and ilView):
ilStatus getPixel(int x, int y, ilPixel& pix, int mode = 0);

In ilView’s version, this function copies the pixel data at the point x,y into pix. If the point
lies outside the view, the fill value is returned by reference. In ilDisplay’s version, the
topmost view pointed to by the point x,y is found with findView(); the pixel data from
the point in that view is copied into pix. If the point refers to no view, no pixel data is
returned by reference. (The x,y point is specified in display coordinates.)
You can also set a pixel value with setPixel():
void setPixel(int x, int y, ilPixel pix, int mode = 0);

182

Managing Views

Locating a Point
You can find out where you are in an image by passing the display coordinates to
getLoc() (defined by both ilDisplay and ilView):
void getLoc(int x, int y, int& ix, int& iy,
int mode = ilLocIn);
void getLoc(float x, float y, float& ix, float& iy,
int mode = ilLocIn);
void getLoc(float& ix, float& iy,
int mode = ilLocIn|ilCenter);

The getloc() function returns the location in the image corresponding to x and y. The
location in the image is returned in ix and iy. If ilLocIn is passed in mode, the location is
returned in the input space of the image. If ilLocOut is passed in mode, the location is
returned in the output space of the image. For example, if an ilRotZoomIng is mapped to
the view and ilLocIn is specified, ix and iy correspond to the location in the unzoomed
image. However, if ilLocOut is specified, ix and iy correspond to the location in the
zoomed image. If ilLocImg is specified (default), then the image is moved to the specified
location. If ilLocView is specified, then the view is moved to the specified location.
The second version uses floating point values for more accuracy. The third version
determines the desired location based on mode. For example, if ilCenter is specified, the
location corresponding to the center of the view is returned.
When called on ilDisplay, the topmost view pointed to by x, y is found with findView().
Then the location is returned for that view. On both ilDisplay and ilView, a version is
provided that does not require an xy location to be specified. Instead, the mode is used to
specify the center or a corner.
Similarly, you can set the location of an image within the display by calling setLoc() on
ilDisplay or ilView. This allows you to move a point within the image to a specific
location within the display, as show below:
void setLoc(int ix, int iy, int x, int y,
int mode = ilLocIn);
void setLoc(float ix, float iy, int mode = ilLocIn|ilCenter);
void setLoc(float ix, float iy, float x, float y,
int mode = ilLocIn);

The relocation can be accomplished by moving the image or the view. If ilLocView is
specified, then the view is moved, otherwise the image is moved (ilLocImg).

183

Chapter 5: Displaying an Image

Applying a Display Operator
Display operators alter views, typically in response to input from the user. These
operators may draw all or portions of a view. Also, they can change the size and/or
location of all or some of the displayed views and then update the display accordingly.
These are ImageVision Library’s display operators; they can be called on both ilDisplay
and ilView (except for display(), which may be called only on ilDisplay):
•

Drawing operators—Operators whose primary purpose is to draw all or part of the
display. This group includes display(), paint(), qpaint(), redraw(),
setStaticUpdate(), and save().

•

Relocating operators—Operators whose primary purpose is to change the location
of views or images. This group includes alignView(), alignImg(), moveView(),
moveImg(), and split().

•

Resizing operators—Operators whose primary purpose is to change the size of
views or images. This group includes wipe(), wipeSize(), wipeSplit(), and resize().

•

update()—Generalized operator that combines the capabilities of moveView(),
moveImg(), and wipe(). However, because it is a generalized operator, it is not as
optimized as some of the other operators.

There is only one difference between calling a function on ilDisplay and calling it on
ilView. When called on ilView, the function operates only on that view regardless of the
state of the nop flag. In contrast, when called on ilDisplay, a view must be specified. If
NULL is passed, then all views in the stack are operated on (except those with the nop
flag set). If a pointer to a view is passed, the function operates only on that view.
In this section, all operators are given in their ilDisplay forms. The ilView versions are
easily derived by leaving out the argument specifying the view.

Drawing Views
The functions used primarily for drawing are described in this section:

184

•

display() reinitializes the specified view and optionally aligns the view and image.
The specified view is then painted. If NULL is specified, then all views are
initialized (except those with nop flag set).

•

paint() does not resize or reposition the view. It simply paints the specified view if it
needs to be painted. If ilPaintExpose is passed, then the view is forced to be painted.

Applying a Display Operator

•

qpaint() queues painted views for multi-processor operations.

•

redraw() resizes the display image and background view to occupy the entire
window. It then paints all views regardless of the nop flag. It does not resize or
reposition any views.

•

save() paints the specified region of the display to an ilImage. A starting location
within the display and a pointer to an ilImage are passed. The save region is
specified by the starting location and the size of the image.

•

setStaticUpdate() sets the staticUpdate mode to paint a rectangular region as one
tile rather than many smaller ones.

display()

The display() function takes three arguments, all of which have default values, as shown
below:
void ilDisplay::display(ilView* view = NULL,
int vmode = ilCenter,
int imode = ilCenter);

view

Reinitializes the specified view. If NULL is passed, then it reinitializes all
views (except those with nop flag set). If the ilDop flag is passed in
mode, the nop flag is ignored.

vmode

Specifies how to align the view within the display.

imode

Specifies how to align the image within the view. As explained above,
only the visible portion of each view is drawn.

Both vmode and imode are a bitwise-OR’d combination of values that allow you to specify
alignment. You can align to any corner or edge using any combination of ilTopEdge,
ilBottomEdge, ilLeftEdge, or ilRightEdge. In addition, ilTopLeft, ilBottomLeft,
ilTopRight, or ilBottomRight can be used to specify a corner. By default, ilCenter is used.
If no alignment is desired, ilNoEdge or ilNoAlign can be passed instead. See “Relocating
Views and Images” on page 188 for more information about the alignView() and
alignImg() functions.
By default, a view is the size of its image; however, if ilClip is passed in vmode, then the
view is clipped to the size of the display or window.

185

Chapter 5: Displaying an Image

paint()

The paint() function is typically used when a view needs to be redrawn after several
deferred operations. This function takes a view pointer and a mode as arguments:
void paint(ilView* view = NULL, int mode = 0);

The view argument has the same meaning as that for display(). The mode argument can
include any of the generic display flags.
You can get the change of position and size from one painting to another using the
getDel() (get delta) function in ilView, defined as follows:
void getDel(iflXYint& dVPos, iflXYint& dVSize, iflXYfloat&
dIPos);
void getDel(iflXYint& dVPos, iflXYint& dVSize, iflXYZfloat&
dIPos);

The first and third references provide the delta of the image’s position since the last
paint(). The second reference provides the delta of the image’s size since the last paint().
The two constructors provide two- and three-dimensional alternatives.
qpaint()

The qpaint() function is used to queue the painting of a specified view. It is used most
often to optimize performance in multi-processor operations. The function is defined as
follows:
void qPaint(ilMpNode* parent, int x, int y, int nx, int ny,
iflOrientation orientation = iflUpperLeftOrigin,
ilView* view = NULL, int mode = 0,
ilMpManager** pMgr = NULL);
void qPaint(ilMpNode* parent, ilView* view = NULL, int mode = 0,
ilMpManager** pMgr = NULL)
{
qPaint(parent,
visArea.x, visArea.y, visArea.nx, visArea.ny,
workOrientation, view, mode, pMgr);
}

The first constructor allows you to define or specify the view being queued. The second
constructor is for use with scrolling lists where the view is clipped by the size of the
scrolling list.

186

Applying a Display Operator

redraw()

The redraw() function is called when a REDRAW (OpenGL) or Expose (X) event occurs
(for example, if the window is exposed or resized):
void redraw(int mode = ilDefault);

The redraw() function resizes the drawing area (display image) and the background
view to match the new size of the window, and paints all views.
save()

The save() function saves a region of the display by painting to an ilImage. The region
saved is specified by the origin x,y and is the size of the image passed in
ilStatus save(ilImage* img, int x = 0, int y = 0,
int mode = ilDefault);

By default, borders are not painted. However, if ilPaintBorder is passed in mode, the
borders are painted. Note that on 8-bit graphics systems, displayed images may be
dithered. Therefore, the save function provides a higher quality result than copying from
the screen.
setStaticUpdate()

The setStaticUpdate() function allows you to enable or disable the staticUpdate mode.
Static update paints a rectangular region as one large tile rather than as many smaller
tiles. When staticUpdate mode is enabled, it forces a static update to occur whenever the
view is painted.
void setStaticUpdate(int enable)

The setAutoStaticUpdate() function forces a static update after a reset has occurred. A
reset is caused by changing inputs or processing parameters in the chain. In this case,
since the entire exposed region of the view must be painted, the performance can be
improved by painting the region as one large tile. After the static update has been
completed, normal tiled painting resumes. By default, automatic static update is enabled.
void setAutoStaticUpdate(int enable)

The isStaticUpdate() function allows you to retrieve the current staticUpdate mode:
isStaticUpdate()

187

Chapter 5: Displaying an Image

Note: Static update mode only has effect for hardware acceleration.

Relocating Views and Images
The functions used to relocate views and images are described in this section:
•

alignImg() aligns an image within its view.

•

alignView() aligns a view with a reference view.

•

moveImg() moves an image within a view.

•

moveView() moves a view within the display area.

•

split() repositions all views into rows and/or columns and resizes the views to fit.

The following mode flags are also used in conjunction with the functions discussed in
this section:
ilAbsVal

The xy pair represents absolute values. In other words, the view is
simply moved to the location specified.

ilDelVal

The coordinates represent a change (delta) in the current view or image
position. For example, if moveView is called with (2,5) and the specified
view is located at (1,1), then the view is moved to (3,6).

ilRelVal

The xy pair is interpreted relative to the starting xy set by calling
setMouse(). The starting x,y values are updated. The setMouse()
function must have been called previously to initialize ilDisplay’s
coordinate values. This is the default mode for most functions.

ilOldRel

Same as ilRelVal except that the starting xy values are not updated.

alignImg()

The alignImg() function is defined on both ilDisplay and ilView. This function aligns the
image in view. If view is NULL (the default), the function aligns the images in all the views
in the view stack (except those with the nop flag set). It is called as shown below:
void alignImg(ilView* view=NULL, int mode=ilCenter);

Alignment means that an edge, corner, or center of an image is aligned within the view,
as shown in Figure 5-6. The mode argument specifies how to align the image. For
example, the default, ilCenter, indicates that the image is to be centered in the view. In

188

Applying a Display Operator

Figure 5-6, ilBottomLeft is passed in mode, causing the lower left corner of the image to
be aligned to the lower left corner of the view.

Image

View

Unaligned Image

Figure 5-6

Aligned Image

Aligning an Image to Bottom Left Corner

alignView()

The alignView() function is defined on both ilDisplay and ilView. This function aligns
the specified view with a reference view. If NULL is passed, all views are aligned (except
those with the nop flag set). The function is called as shown below:
void alignView(ilView* view = NULL, int mode = ilCenter,
ilView* rView = NULL);

The reference view is specified by rview. If it is NULL, then the back view is used.
Alignment means that edges, corners, or centers of the views are aligned, as shown in
Figure 5-7. The mode argument specifies how to align the views. By default, ilCenter
causes views to be aligned by their centers. In Figure 5-7, the views are aligned by their
lower left corners with ilBottomLeft.

Display

Reference
View

Unaligned Views

Figure 5-7

Aligned Views

Aligning Views

189

Chapter 5: Displaying an Image

moveImg()

The moveImg() function changes the location of images within their respective views. To
use this function, you need to specify the desired location and the view to which the
image corresponds:
void moveImg(float x, float y, ilView* view = NULL,
int mode = ilRelVal);

This function moves the image within the specified view. In other words, the view
remains fixed relative to the display while the corresponding image moves within the
view. This function allows a user to roam around an image. This is particularly useful for
large images that are bigger than the screen. Thus, the coordinate values x,y specify the
desired location of the image’s origin. they are interpreted according to the relevant flags
passed in mode (such as ilDelVal, ilRelVal, and so on). The mode argument can also include
flags indicating that drawing should be deferred (ilDefer) and that the image should not
be moved beyond its edge (ilClip). By default, the image can be moved beyond its view,
in which case the image’s fill value is used to paint the view.
When roam is enabled, the speed with which you can roam around a picture is related to
the displacement of the mouse: the farther you move the mouse, the greater the
displacement between consecutively-displayed frames.
You can change this behavior by calling setRoamLimit(). This function limits the
displacement between consecutively-displayed frames for example, if the limit is set to
four, regardless of how far you move your mouse, consecutively displayed frames will
always be displaced by four pixels. This function has the effect of smoothing out roam
motion.
float getRoamLimit();
void setRoamLimit(float maxRoamDel = 0.0);
void getRoamRate(float& x, float& y);

The getRoamRate() returns the displacement, in pixels, in the X and Y directions
between consecutively-displayed frames.

190

Applying a Display Operator

moveView()

The moveView() function changes the location of views within the display. You might
use this function to allow a user to drag a view around the display area using one of the
mouse buttons. To use this function, specify the desired location and the view to be
moved:
void moveView(int x, int y, ilView* view = NULL,
int mode = ilRelVal);

The view pointer argument view specifies which view to move (or all the views if NULL,
the default). The x and y arguments indicate where to move the view, and mode specifies
how these arguments should be interpreted (with ilDelVal, ilRelVal, and so on).
You can include ilDefer in the mode argument if you do not want the display updated.
Also, by default, you can move the views out of the window. For example, a user can
continue dragging a view past the edge of the window; the view will not be visible, but
ilDisplay keeps track of its location so that if the user drags it in the opposite direction,
eventually the view becomes visible in the window. You can prevent a view from being
moved past the window’s edge by specifying ilClip as part of the mode argument.
split()

The split() function allows you to display all views next to one another in rows and/or
columns rather than randomly overlapping one another. All views are resized and
repositioned based on the number of views in the view stack. Starting at the bottom of
the stack, views are positioned starting at the lower left corner of the display. The split()
function is called as shown below:
void split(int mode = ilAbsSplit|ilRowSplit|ilColSplit)

The mode argument controls the layout. It can be a combination of the following modes:
ilAbsSplit

Aligns images to the origin regardless of the view position. (See
Figure 5-8.)

ilRelSplit

Positions images relative to view position. (See Figure 5-9.)

ilRowSplit

Divides the drawing area into rows. (See Figure 5-8.)

ilColSplit

Divides the drawing area into columns.

ilPackSplit

Clips views to an image if needed and packs them together.

191

Chapter 5: Displaying an Image

Views to be displayed

Displayed with the
default mode ilAbsSplit |
ilRowSplit | ilColSplit

Displayed with
iilAbsSplit | ilColSplit

Figure 5-8

split() with ilAbsSplit | ilRowSplit | ilColSplit

Figure 5-9

split() with ilRelSplit | ilRowSplit | ilColSplit

If both ilRowSplit and ilColSplit are specified, split() divides the drawing area into
equal-sized rectangles such that the number of rows and columns is nearly equal. (See
Figure 5-9.) Note that if both ilAbsSplit and ilRelSplit are specified, split defaults to
ilAbsSplit. In addition, an alignment mode can be specified with ilAbsSplit, such as
ilCenter.

192

Applying a Display Operator

Resizing Views
The functions used to resize one or more views are shown below and are described in this
section:
•

resize() resizes a view (defined only on ilView).

•

wipe() moves one or more edges of a view.

•

wipeSplit() wipes the nearest edge of all views.

•

wipeSize() wipes an edge or corner and the opposite edge or corner.

As with the relocating functions, if ilAbsVal is passed in mode, the xy values specify the
new size of the view. For ilDelVal, the xy values represent changes to the current size of
the view. ilRelVal means that the xy values are interpreted relative to the start values
previously set with setMouse(). The start values are then updated by ilDisplay unless
ilOldRel is specified.
resize()

The resize() function reinitializes the size of a view to the size of the image it displays.
This useful after setting the image in ilView. The resize() function is called as shown
below:
void resize(int mode = 0);

If ilClip is passed in mode, then the view is clipped to the size of the display. After the
view is resized, it is painted unless ilDefer is passed.
wipe()

The wipe() function moves one or more edges on the specified view. It is called as shown
below:
void wipe(int x, int y, ilView* view = NULL,
int mode = ilRelVal);

The values x and y specify how to move the specified edge of the view. they are
interpreted according to the flags passed in mode (such as ilRelVal, ilDelVal, and so on).
The default is ilRelVal. If NULL is passed for view, then all views are wiped (except those
with nop flag set).

193

Chapter 5: Displaying an Image

The edge to wipe is specified in mode. Any combination of the following edge modes can
be used: ilRightEdge, ilLeftEdge, ilTopEdge, or ilBottomEdge. For example,
ilTopEdge|ilRightEdge (or ilTopRight) allows the user to wipe the upper right corner,
thus resizing the view. In addition, the value returned by findEdge() can be used directly.
(See “Finding an Edge” on page 181.)
If ilAllEdge (or a bitwise OR of all four edges) is used, the effect is slightly different from
a normal wipe. In this mode, called an inset, the view moves while the image remains
fixed (opposite of moveImg()). This mode is useful to move a processed view of an image
around on top of the original image for comparison.
By default, the view is painted after it is wiped unless ilDefer is passed in mode. Also by
default, the edge of a view can be moved beyond the edge of the image, unless ilClip is
passed. When the view is allowed to be wiped beyond the edge of the image, the image’s
fill value is used to paint the exposed region. Note that the wipe function is optimized to
paint only the wiped region.
wipeSplit()

The wipeSplit() function is used in conjunction with findEdge() on ilDisplay to wipe the
nearest edge of all views. It is called as shown below:
void wipeSplit(int x, int y, int mode = ilRelVal);

The x and y parameters control how the edges are moved. No view is specified because
it operates on all views in the view stack. The mode parameter specifies only how to
interpret x and y. Note that the edge on each view is not specified by mode. Instead,
findEdge() must be called on ilDisplay first to find the edge on all views. If no edge is
found for a particular view, then that view is not wiped.
This function is useful after a split operation. For example, if the display is split to show
two views side by side, it allows you to wipe the right edge of the left view and the left
edge of the right view simultaneously. This is useful when comparing two or more
images. In general, adjacent views can be wiped using this function.
wipeSize()

The wipeSize() function wipes the specified edge and the opposite edge to resize the
view. It is called as shown below:
void wipeSize(int x, int y, ilView* view = NULL,
int mode = ilDelVal | ilTopRight);

194

Applying a Display Operator

The x and y parameters control which way to move the edge specified in mode. In
addition, the opposite edge is moved in the opposite direction, causing the view to grow
or shrink in size. For example, if the right edge is moved to the right, then the left edge
is moved to the left as well. In this case, the view would grow in width, as show in
Figure 5-10.

Previous size of view
New wiped size of view

Figure 5-10

Using wipeSize()

Updating Views
The update() function can change the view position, view size, and image position as
shown below:
void update(int x=0, int y=0, int nx=0, int ny=0,
int imgX=0, int imgY=0,
ilView* view=NULL, int mode=ilRelVal);

The view is moved to the position specified by x and y and is resized to nx and ny. The
image within the view is moved to the position specified by imgX and imgY. If view is
NULL, then all views in the view stack are updated (except those with nop flag set).
The first six of these parameters are interpreted as specified by mode. For example, if
ilDelVal is specified, then all six parameters are interpreted as changes from the current
configuration. In addition, the parameters are used as specified. However, if ilClip is
passed in mode, then the view position, size, and image position are clipped. After the
view has been updated, it is painted unless ilDefer is passed in mode. The update function
combines the functionality of moveView(), wipe(), and moveImg().

195

Chapter 5: Displaying an Image

Using setMouse()
A display support function that you might find useful as you apply display operators is
setMouse():
void setMouse(int x, int y, int mode = 0);

This function is typically used in an interactive loop to initialize the starting x and y
coordinate values that the ilDisplay keeps track of. The coordinates passed to any
function with ilRelVal or ilOldRel are interpreted relative to the current mouse position.
If ilRelVal is specified, the old start values are updated; however, if ilOldRel is specified,
the start values are not updated. This is useful if several operations are needed and you
do not want to update the start values until you are finished. This model is used in the
program presented in “A Simple Interactive Display Program” on page 165. To retrieve
the previously set start values, use getMouse(). This function returns the start values by
reference:
void getMouse(int& x, int& y);

You can achieve many different effects by judiciously deferring drawing while you apply
a combination of these and/or any of the other display operators.

A More Complicated Interactive Display Program
The ilview interactive display program (which is installed in /usr/sbin when you install
the Image Tools) allows a user to drag, roam, and wipe several images in a display
window. (See ilview’s reference page for more information.) A simplified version of
ilview’s source code is provided online in:
/usr/share/src/il/ilview.c++
The C version of this program named ilcview.c is located in the same directory.
Example 5-2 shows the portion of this program that processes events and calls display
operators. It uses an ilViewer to handle events. The ilViewer class is a higher-level object
derived from ilDisplay. It calls ilDisplay functions and operators based on X events. It
calls moveView() for left mouse button movement and moveImg() for middle mouse
button movement. The cursor changes shape near the edges and corners to indicate that
wipe mode is enabled on the left mouse button. If you press the left mouse button and
perform a wipe, this changed cursor remains for the duration of the wipe. See the
ilViewer reference page and the header file il/ilViewer.h for details.

196

A More Complicated Interactive Display Program

Example 5-2

A More Complicated Interactive Display Program

// Create X window viewer
ilViewer viewer(dpy, winsize.x, winsize.y, attr);
for (idx = 0; idx < nimg; idx++)
viewer.addView(img[idx], ilLast, ilClip|ilCenter|ilDefer);
viewer.setStop(TRUE);
// Execute the UI event loop
// XXX need event call back on ilViewer to make this easier to do
int done=FALSE;
while (!done) {
XEvent e;
XNextEvent(dpy, &e);
switch (e.type) {
case KeyPress:
switch(XLookupKeysym(&e.xkey, 0)) {
// center the selected view(s) in the viewer
case XK_Home:
viewer.display(NULL, ilCenter|ilClip);
break;
// control-Q and escape exit the program
case XK_q:
if (!(e.xkey.state&ControlMask))
break;
/*FALLTHROUGH*/
case XK_Escape:
done = TRUE;
break;
// raise and lower the current view(s)
case XK_Up:
viewer.raise();
break;
case XK_Down:
viewer.lower();
break;
// enable/disable paint pipelining
case XK_p:

197

Chapter 5: Displaying an Image

viewer.enableQueueing(!viewer.isQueueingEnabled());
break;
}
break;
case DestroyNotify:
viewer.destroyNotify();
done = TRUE;
break;
default:
viewer.event(&e);
break;
}
}
exit(EXIT_SUCCESS);
}

198

Chapter 6

6. Extending ImageVision Library

Since ImageVision Library (IL) is implemented in C++, you can easily extend it by
deriving new classes that provide support for the capabilities you need; for instance, to
include another file format or image processing algorithm. You can derive from any C++
class, but you are most likely to want to derive from the foundation classes. Figure 6-1
shows the types of classes you are most likely to derive.
Note: If you are using the C interface to IL, extending the library is not quite so simple.

You have to implement a new class in C++ and then generate a C interface for it.
This chapter contains the following major sections:
•

“Deriving From ilImage” on page 202 tells you how to derive new classes from
ilImage.

•

“Deriving From ilCacheImg” on page 212 tells you how to derive new caching
classes to manage data.

•

“Deriving From ilMemCacheImg” on page 213 tells you how you can derive from
ilMemCacheImg to manage images in main memory.

•

“Implementing an Image Processing Operator” on page 215 tells you how to define
operators that implement new image processing algorithms.

•

“Deriving From ilRoi” on page 242 describes how you define new regions of
interest in your images.

199

Chapter 6: Extending ImageVision Library

IL classes from which you might want to derive your own new classes are shown in
Figure 6-1.

newFileFormat

..

iflTIFFFile

iflFormat

newImgClass
iflJFIFFILE
ilFileImg
ilMemoryImg

IFLfitfILE

IFLrAWfILE
ilLink

ilImage

ilCacheImg

ilMemCacheImg

ilFrameBufferImg

ilOpImg

..

ilSharpenImg

..

ilRotZoomImg

..

newOperator

ilImgRoi
ilRoi
ilRectRoi

newRoiClass

Figure 6-1

newCacheClass

User-Defined Classes in IL

Each extension to IL can be designed to provide a certain set of capabilities and require
the implementation of a matching set of functions, as described below:
•

200

newImgClass—A class derived from ilImage inherits all of its functions for
handling an image’s attributes; it needs to implement ilImage’s pure virtual

functions for reading and writing data. More information on deriving from ilImage
is provided in “Deriving From ilImage” on page 202.
•

newCacheClass—A class derived from ilCacheImg inherits its caching mechanism;
such a class is useful for managing a large amount of data that is accessed a portion
at a time. More information on deriving from ilCacheImg is provided in “Deriving
From ilCacheImg” on page 212.

•

new ilMemCacheImg class—A class derived from ilMemCacheImg inherits its main
memory caching mechanism. Implement the pure virtual functions for storing and
retrieving pages of image data. More information on deriving from
ilMemCacheImg is provided in “Deriving From ilMemCacheImg” on page 213.

•

newOperator—To define a new operator, you need to implement the desired image
processing algorithm and ensure that the processed image has the correct attributes.
You can derive directly from ilOpImg or from one of its generalized subclasses. See
“Implementing an Image Processing Operator” on page 215 for more information.

•

newRoi—To define a new region of interest (ROI), you need to derive from ilRoi
and implement functions that describe valid and invalid regions with respect to this
new ROI. See “Deriving From ilRoi” on page 242 for more information.

The classes ilImage, ilCacheImg, ilMemCacheImg, ilOpImg, and ilRoi declare virtual
functions that subclasses may be redefined to alter class behavior. Other functions can be
added as necessary to provide the desired capabilities of the class.
The remaining sections in this chapter explain how to derive from ilImage, ilCacheImg,
ilMemCacheImg, iflFileImg, ilOpImg, or ilRoi (or one of their generalized subclasses).
Remember that when you derive from a class, you inherit all its public and protected
data members and member functions, as well as the public and protected members from
its superclasses. You should review beforehand the header files and the reference pages
for any class you plan to derive from in order to become familiar with its data members
and member functions. Many of the functions described in the following sections are
protected, so they are available for use only by derived classes.

201

Chapter 6: Extending ImageVision Library

Deriving From ilImage
A class derived from ilImage must assign values to the image’s attributes and implement
ilImage’s virtual functions. The image’s attributes (data members) are listed in Table 6-1;
they are generally initialized in the constructor.
Table 6-1

Image Attributes Needing Initialization in ilImage Subclass

Name

Data Type

Meaning

pageSize

iflSize

size of the image’s pages in pixels

dtype

iflDataType

pixel data type

order

iflOrder

pixel data ordering

cm

iflColorModel

image’s color model

orientation

iflOrientation

location of origin and orientation of axes

fillValue

iflPixel

value used to fill pixels beyond the image’s edge

minValue,
maxValue

double

minimum and maximum allowable pixel values

status

ilStatus

image’s status (for example, ilOKAY)a

a. Inherited from ilLink.

Typically, you will just set these attributes directly. However, there are convenience
functions—for setting minValue, maxValue, cm, and status—that you might want to use
(these functions are protected, so they are available only to classes derived from ilImage):
void initMinMax(int force=FALSE);
void initColorModel(int noAlpha=FALSE);
void initPagesize(const iflSize& pageSize);
ilStatus setStatus(ilStatus val); //inherited from ilLink
void clearStatus();
// inherited from ilLink

The initMinMax() function simultaneously sets both the minimum and maximum
allowable pixel values. They are set to the smallest and largest possible values,
respectively, allowed by the image’s data type. Therefore, you must set the image’s data
type before you call initMinMax(). By default, this function’s argument is FALSE, which
means that the minimum and maximum values will not be changed if they have already

202

Deriving From ilImage

been explicitly set; if you pass in TRUE as the argument to this function, both values will
be set regardless of whether they have been set before.
The initColorModel() function sets the color model based on the channel dimension of
the image. If the channel dimension is 1, the color model is iflLuminance; if it is 2, the
color model is iflLuminanceAlpha (or iflultiSpectral if noAlpha is TRUE); if it is 3, the
color model is iflRGB. If the channel dimension is 4 and the default value of FALSE is
used for the noAlpha argument, the color model is iflRGBA. Otherwise, the color model
is iflMultiSpectral.
The setStatus() function simply sets and returns the image’s status. The clearStatus()
function sets the image’s status to ilOKAY. (Both of these functions are inherited from
ilLink.) See “Error Codes” on page 366 for a list of the error codes that IL defines as being
of type ilStatus.
Another function you may want to use in a constructor is setNumInputs(). This function
sets the maximum possible number of inputs to an image. Typically, you will use this
function only when deriving an operator. See “Implementing an Image Processing
Operator” on page 215 for more information about doing this.

Data Access Functions
Image data can be accessed as pixels or as a rectangular region of arbitrary size called a
tile. Both 2-D and 3-D tile access functions are provided.
The virtual access functions present a queued request model, which allows an
application to issue non-blocking requests for image I/O and later inquire the status or
wait for the operation to complete. The queued model also provides derived classes with
the “hooks” needed to automatically distribute operations across multiple processors.
These queued functions are distinguished by the prefix “q” on the function name. For
convenience, there are access functions that do wait for their operation to complete,
hiding the details of the queued model.
There are several different functions to read image data, all based on qGetSubTile3D().
ilQGetSubTile3D(). Similarly, there are several different functions to write image data
based on qSetSubTile3D(). ilQSetSubTile3D(). Two fast-paths called qCopyTileCfg() and
qCopyTile3D() ilQCopyTileCfg() and ilQCopyTile3D() are available for copying a tile
from another ilImage.

203

Chapter 6: Extending ImageVision Library

Most of the virtual functions in ilImage are data access functions:
virtual ilStatus qGetSubTile3D(ilMpNode* parent, int x, int y, int z,
int nx, int ny, int nz, void*& data, int dx, int dy, int dz, int dnx,
int dny, int dnz,const ilConfig* config=NULL, ilMpManager** pMgr=NULL);
virtual ilStatus qSetSubTile3D(ilMpNode* parent, int x, int y, int z,
int nx, int ny, int nz, void* data, int dx, int dy, int dz, int dnx,
int dny, int dnz, const ilConfig* config=NULL, ilMpManager** pMgr=NULL);
virtual ilStatus qCopyTileCfg(ilMpNode* parent, int x, int y, int z,
int nx, int ny, int nz, ilImage* other, int ox, int oy, int oz,
const ilConfig* config=NULL, ilMpManager** pMgr=NULL);
virtual ilStatus qDrawTile(ilMpNode* parent, int x, int y, int nx, int ny,
ilImage* src, float sx, float sy, float sz, ilMpManager** pMgr=NULL);
virtual ilStatus qFillTile3D(ilMpNode* parent, int x, int y, int z,
int nx, int ny, int nz, const void* data, const ilConfig* config=NULL,
const iflTile3Dint* fillMask=NULL, ilMpManager** pMgr=NULL);
virtual ilStatus qFillTileRGB(ilMpNode* parent, int x, int y, int z,
int nx, int ny, int nz, float red, float green, float blue,
const iflTile3Dint* fillMask=NULL,
iflOrientation orientation=iflOrientation(0), ilMpManager** pMgr=NULL);
virtual ilStatus qLockPageSet(ilMpNode* parent, ilLockRequest* set,
int mode=ilLMread, int count=1, ilMpManager** pMgr=NULL,
ilCallback* perPageCb=NULL);
ilStatus qGetTile3D(ilMpNode* parent, int x, int y, int z, int nx, int ny,
int nz, void*& data, const ilConfig* config=NULL, ilMpManager** pMgr=NULL)
ilStatus qSetTile3D(ilMpNode* parent, int x, int y, int z, int nx, int ny,
int nz, void* data, const ilConfig* config=NULL, ilMpManager** pMgr=NULL)

When calling the base functions listed above, the caller must specify the origin (x, y, z)
and size (nx, ny, nz) of the desired tile. For 2-D operations, z is set to 0 and nz is set to 1.
For pixel operations, nx, ny and nz are set to 1. An object called iflConfig, is used to
specify the configuration (that is, data type, order, number of channels and so forth) of
the desired tile. If required, the image data is converted to a specified configuration while
getting a tile, or converted from a specified configuration to that of the image while
setting a tile.

204

Deriving From ilImage

All of these functions have default implementations that you can choose to override. The
rest of this section explains how to implement these functions.
Implementing qGetSubTile3D()

You should implement qGetSubTile3D() so that it retrieves an arbitrary tile of data from
the source image and puts it into the location indicated by data. The tile is located at
position (x, y, z) in the source image and has the size indicated by nx, ny, and nz. The dx,
dy, and dz parameters specify the data buffer’s origin relative to the image; dnx, dny, and
dnz specify the buffer’s size. The optional config argument indicates how the data should
be configured in the buffer. See “Three-dimensional Functions” on page 46 for more
information about qGetSubTile3D().
This function has a default implementation that returns ilUNSUPPORTED.
Implementing qSetSubTile3D()

Your version of the qSetSubTile3D() function should write the tile of data pointed to by
data into the destination image. The arguments for qSetSubTile3D() have analogous
meanings to those for qGetSubTile3D(): (x,y,z) and (nx, ny, nz) indicate the desired
origin and size of the tile in the destination image; dx, dy, and dz specify the data buffer’s
origin relative to the image; and dnx, dny, and dnz specify the size of the data buffer. The
optional config argument describes the configuration of the tile being passed or written;
if it is NULL, assume that the tile’s configuration matches that of the destination image.
See “Three-dimensional Functions” on page 46 for more information about
qSetSubTile3D().
This function has a default implementation that returns ilUNSUPPORTED.
Implementing qCopyTileCfg()

The default implementation of qCopyTileCfg() copies a tile of data from one image to
another. This implementation is not as efficient as possible, since it allocates a temporary
buffer for holding the data as it performs the copy and then deletes the buffer when it
completes the copy. You might want to override this function to provide a more efficient
version.

205

Chapter 6: Extending ImageVision Library

Implementing qFillTile3D() and qFillTileRGB()

The default versions of qFillTile3D() and qFillTileRGB() do nothing; you will need to
override them if you want their functionality. Your implementations should fill a
specified tile with the specified pixel value or color.
Implementing qLockPageSet()

Your implementation of qLockPageSet() should set a read-only lock for a set of pages
when accessing image data. A pointer to each page in the set is deposited in each
corresponding ilLockRequest. As a result, the image data for all of the pages is
computed. If all of the requests succeed, ilOKAY is returned. If one or more fail, an error
code will be returned and the ilLockRequest structures will contain individual status
codes.
Implementing qGetTile3D()

This function places the destination of a tile, pointed at by data, at coordinates, x, y, z
using the size of the source image defined by dx, dy, dz.
Your class must overwrite qGetTile3D(). Its default function returns ilUNSUPPORTED.
Implementing qSetTile3D()

This function allows the source buffer to have a different position and size, specified by
dx, dy, dnx, dny, dz, and dnz.
Your class must overwrite qGetTile3D(). Its default function returns ilUNSUPPORTED.
Support Functions

The outOfBound() support functions are provided to help implement the data access
functions:
int outOfBound(int x, int y);
int outOfBound(int x, int y, int z);

These functions return TRUE if the specified point lies outside the image.
If you implement any of the data access functions, you need to hook them into the reset
mechanism, which is described next.

206

Deriving From ilImage

Color Conversion
The checkColorModel() function matches the color model of an image with the number
of channels. If there is a mismatch, the number of channels is updated to match the color
model. However, if the number of channels was set and there is a mismatch, a status of
ilBADCOLFMT is set.
void checkColorModel();

The needColorConv() function returns TRUE if the image’s color model does not match
the color model of other. The from flag indicates the direction that data is copied:
needColorConv(ilImage* other, int from, const ilConfig* cfg);

The getCopyConverter() function chains one image to another provided the two images
have different color models. If the images have the same color model, there is no color
conversion. getCopyConverter() is defined as follows:
int getCopyConverter(ilImage*& other,const ilConfig* cfg)

The getCopyConverter() function returns TRUE if the other image has a different color
model than this image. In this case, a color converter operator is chained onto the other
image.
The getCopyConverter() function returns FALSE if the color models are compatible, or
if the cfg specifies a channel list or channel offset. In this case a converter operator is not
chained to the other image.When cfg specifies a channel list or offset, no color conversion
is performed.

Managing Image Attributes
An image has numerous attributes associated with it that describe the image. You can
change some attributes; some change as a side effect of changing some other attribute.
This section describes functions you can use to manage attribute values in a class derived
from ilImage.

207

Chapter 6: Extending ImageVision Library

The reset() Function

An important virtual function in ilImage that you must be concerned with is reset():
virtual void reset(); // inherited from ilLink

This function is designed to adjust or validate an image’s attributes if they have been
altered, for example, by applying an operator or by setting an attribute explicitly. This
function plays a key role in IL’s execution model, which propagates image attribute
values down an operator chain. (See “Propagating Image Attributes” on page 59 for
more information on propagating image attributes.)
The reset mechanism is triggered whenever an image is queried about its attributes or
when its data is accessed. The query and access functions all call resetCheck() (which is
inherited from ilLink) to initiate the reset process. If you implement qGetSubTile3D(),
qSetSubTile3D(), qCopyTileCfg(), qFillTile3D(), qFillTileRGB(), qLockPageSet(),
qGetTile3D(), qSetTile3D() or any attribute query, you need to call resetCheck() before
you do anything else in your versions of these functions. This ensures that correct
information about an image’s attributes is returned and that image data is always valid
before it is read, written, copied, filled, or updated.
The reset() function must be defined by derived classes to perform any necessary reset
tasks. For example, the ilMemCacheImg class’s version of reset() throws out any existing
data in the cache since it is invalid; ilOpImg performs several chores in its reset() function
and then calls resetOp(), which needs to be implemented by derived classes to perform
more specific reset tasks.
Allowing Attributes to Change

Not every image attribute can be changed; by default, the fill value and the maximum
and minimum pixel values are allowed to change. Each ilImage derived class can choose
which attributes it allows to be modified by using the setAllowed() function (inherited
from ilLink), typically in the constructor:
setAllowed(ilIPcolorModel|ilIPorientation);

The argument passed to setAllowed() is a mask composed of a logical combination of the
enumerated type, ilImgParam, which is defined in the header file il/ilImage.h. The
ilImgParam constants defined in IL are listed in Table 6-2. Each image attribute listed in
the table is described elsewhere in this guide. Derived classes can add members to this

208

Deriving From ilImage

structure to trace whether particular parameter values have changed and to control
whether they can be explicitly modified.
Table 6-2

ilImgParam Constants

Defining Class

ilImgParam

Image Attribute

ilImage

ilIPdataType

data type

“

ilIPorder

pixel ordering

“

ilIPpageSize

page size

“

ilIPxsize

x dimension of page size

“

ilIPysize

y dimension of page size

“

ilIPzPageSize

z dimension of page size

“

ilIPxyPageSize

x,y dimension of page size

“

ilIPcPageSize

component value of a pixel

“

ilIPpageSize

red values of ilIPzPageSize,
ilIPxyPageSize, and ilIPcPageSize

“

ilIPchans

number of channels

“

ilIPdepth

z dimension of the image

“

ilIPorientation

orientation

“

ilIPcolorModel

color model

“

ilIPminValue

minimum pixel value

“

ilIPmaxValue

maximum pixel value

“

ilIPscale

color scaling value

“

ilIPfill

fill value

“

ilIPcompression

compression

“

ilIPcmap

look-up table color map

“

ilIPpageBorder

page border for overlapping pages

ilFileImg

ilFPimageIdx

image index

209

Chapter 6: Extending ImageVision Library

Table 6-2 (continued)

ilImgParam Constants

Defining Class

ilImgParam

Image Attribute

ilOpImg

ilIPbias

bias value

“

ilIPclamp

clamp value

“

ilIPworkingType

working data type

ilSubImg

ilIPconfig

configuration

ilImgStat

ilISPzBounds

z dimension bounds

ilRoi

ilROIorientation

orientation

Preventing Attributes From Changing

An image can explicitly disallow any of these attributes to be modified. For this, it uses
the clearAllowed() function (from ilLink) and passes in a logical combination of the
ilImgParam parameters that should be disallowed.
Another function, isAllowed() (inherited from ilLink), checks whether a particular
attribute can be modified:
canChange = myImg.isAllowed(ilIPsize);

This function takes the same sort of argument as clearAllowed() and returns TRUE if the
attributes specified are not allowed to be modified.
Setting Altered and Stuck Flags

When an attribute’s value is changed by the user (by calling the appropriate attribute
setting function), setAltered() (from ilLink) should be called to set a flag indicating that
a reset is needed. Thus, you must call setAltered() within any attribute setting functions
you define. This function takes a mask of ilImgParam parameters as an argument and
sets the altered flags for the specified attributes.
You can check whether any particular attributes have been altered with isAltered()
(inherited from ilLink). This function takes an ilImgParam mask as an argument and
returns TRUE if any of the specified attributes have been altered.
As explained in “Propagating Image Attributes” on page 59, IL programs need to keep
track of attributes that have been explicitly set by the user so that they remain fixed

210

Deriving From ilImage

during the reset process. To keep track of these attributes, you should call markSet()
(inherited from ilLink) with an ilImgParam mask as an argument. This function marks
the specified attributes with a stuck flag (yet another item inherited from the ilLink class),
which indicates that their values should not be changed during a reset operation.
markSet() is invoked automatically for you when setAltered() is called, so generally you
do not need to call markSet() yourself.
You can determine whether any attributes are fixed with isSet() (inherited from ilLink).
This function returns TRUE if any of the attributes specified in the mask passed in have
been explicitly set.
Setting Attributes Directly

Sometimes within a derived class’s implementation, you may want to change an
attribute’s value without triggering the reset mechanism and without causing the value
to become fixed. You have already seen one situation where you want to do this: within
a constructor, when attributes are being initialized. Another case is when you are
computing attribute values during the reset operation itself. In these situations, you do
not use a attribute setting function since it calls setAltered(), which in turn calls
markSet(). Since derived classes have access to protected data members, simply set the
value of the desired attribute directly:
dtype = iflFloat;

// changes value; no flag set

The initMinMax(), initColorModel(), and setStatus() functions described earlier in this
section all set attributes directly.
Adding New Attributes

It is quite easy to add attributes to a newly derived class. You can use the header files for
the already existing IL classes for examples. This an example is from the il/ilOpImg.h
header file:
enum ilOpImgParam {
ilIPbias = ilImgParamLast<<1,
ilIPclamp = ilImgParamLast<<2,
ilIPworkingType = ilImgParamLast<<3,
ilOpImgParamLast = ilIPworkingType
};

211

Chapter 6: Extending ImageVision Library

The pattern is simple. Suppose you were to derive a new class from ilOpImg and add
parameters to it. You might do the following:
enum ilMyClassParam {
ilIPparam1 = ilOpImgParamLast<<1,
ilIPparam2 = ilOpImgParamLast<<2,
ilIPparam5 = ilOpImgParamLast<<5,
ilMyClassParamLast = ilIPparam5
};

Deriving From ilCacheImg
The ilCacheImg class implements an abstract model of cached image data. The main
purpose of this class is the definition of a common API for cached image objects. You can
implement your own caching mechanism by deriving from ilCacheImg. The
ilMemCacheImg class, derived from ilCacheImg, provides an example of the
implementation of a caching mechanism.
If you derive from ilCacheImg, you must implement the data access methods inherited
from ilImage. You must also implement the flush(), getCacheSize(), and listResident()
functions if you derive from ilCacheImg.
The flush() function causes any modified data in the cache to be written out. Derived
classes that access an image file can call this function in their destructor before they close
the file to ensure that all data is written:
virtual ilStatus flush(int discard=FALSE);

The getCacheSize() function returns the amount of cache memory, in bytes, currently
allocated by this image object:
virtual size_tgetCacheSize();

The listResident() function returns a list of all the resident pages:
virtual ilStatus listResident(ilCallback* cb);

The callback specified in cb is invoked once for each page resident in memory. The
callback function should have prototype as defined in addPagingCallback().

212

Deriving From ilMemCacheImg

Deriving From ilMemCacheImg
The ilMemCacheImg class implements a caching mechanism for efficiently manipulating
image data in main memory. In managing the interface to an image’s cache,
ilMemCacheImg implements all of the ilImage virtual data access functions. The
ilMemCacheImg class also implements the virtual function hasPages(), which is defined
in ilImage. hasPages() should return TRUE only for classes that implement IL’s paging
mechanism (ilMemCacheImg does).
Classes that derive from ilMemCacheImg do not need to implement these functions;
instead, they need to implement some or all of the following virtual functions:
virtual
virtual
virtual
virtual
virtual

ilStatus
ilStatus
ilStatus
ilStatus
ilStatus

prepareRequest(ilMpCacheRequest* req);
executeRequest(ilMpCacheRequest* req);
finishRequest(ilMpCacheRequest* req);
getPage(ilMpCacheRequest* req);
setPage(ilMpCacheRequest* req);

Image data requests are processed through the multi-processing scheme defined by the
ilMpManager and ilMpRequest classes. The virtual functions, prepareRequest(),
executeRequest(), and finishRequest(), define the API for multi-processing. To maintain
the multi-processing scheme, you must sub-divide processing operations into these three
stages:
•

prepareRequest() allocates buffer space for the pages an operator will work on and
loads the image data from those pages into the buffer.

•

executeRequest() performs the image manipulation on the pages in the buffer.

•

finishRequest() deallocates the buffer space allocated in prepareRequest() and
unlocks the input pages.

Derived classes must re-define these virtual functions. These functions are described in
greater detail in “Handling Image Processing” on page 221.
The ilMpCacheRequest class (defined in the header file il/ilMemCacheImg.h) defines the
page’s location within the image and the amount of data to be processed:
class ilMpCacheRequest : public ilMpRequest, public iflXYZCint {
public:
ilMpCacheRequest(ilMpManager* parent, int x, int y, int z, int c,
int mode = ilLMread);

213

Chapter 6: Extending ImageVision Library

// methods to access mode fields
int isRead() { return mode&ilLMread; }
int isWrite() { return mode&ilLMwrite; }
int isSeek() { return mode&ilLMseek; }
int getPriority() { return mode&ilLMpriority; }
// method to access page data
void* getData() { return page->getData(); }
int nx, ny, nz, nc; // size of valid data in page
};

Since an image’s size is not generally an exact multiple of the page size, you are likely to
encounter pages that are only partially full of data. The nx, ny, nz, and nc members define
the actual limits of the data that you need to read or write within a given page buffer. You
might want to use the getStrides() function to help you step through a page buffer. See
“Data Access Support Functions” on page 47 for more information about getStrides().
Table 6-3 lists additional attributes you might need to initialize for a class derived from
ilMemCacheImg.
Table 6-3

Additional Attributes Needing Initialization in ilMemCacheImg Derived Classes

Name

Data Type

Meaning

pageSizeBytes

size_t

size of a page in bytes

pageSize

iflSize

pixel dimensions of the pages used to store data on
disk

pageBorder

iflXYZint

pixel dimensions of page borders as stored on disk
(default is zero)

You can also implement the allocPage() and freePage() functions. These functions
allocate or free a page in main memory whose pixel includes (x,y,z,c). If you implement
the function allocPage(), you must also call the function doUserPageAlloc() in the
function that calls allocPage() to notify IL that the pages need to be defined.
The flush() function (defined by ilMemCacheImg) flushes data from an image’s cache; it
calls setPage() to ensure that the data is written to the proper place:
virtual ilStatus flush(int discard=FALSE);

214

Implementing an Image Processing Operator

This function takes one optional argument and returns an ilStatus to indicate whether the
flush was successful. Calling flush() with a TRUE argument discards all data in the cache.
This is useful for freeing up memory if you know you are never going to use the cached
data again. When discard is FALSE, flush() writes any modified data from the cache to
the image. The destructor for any class derived from ilMemCacheImg may need to call
ilMemCacheImg’s flush() (with discard equal to FALSE) before the class object is deleted
to ensure that any modified data is written back to the image.
For more information about deriving from either of ilMemCacheImg’s derived class
ilOpImg, see “Implementing an Image Processing Operator” on page 215.

Implementing an Image Processing Operator
IL is designed to be easily extendable in C++ to include image processing algorithms you
implement. You can derive a new operator directly from ilOpImg, or you can take
advantage of the support provided by its subclasses, some of which are specifically
designed to be derived from. This section explains in detail how to derive your own
operator. It contains these sections:
•

“Deriving From ilOpImg” on page 217

•

“Deriving From ilMonadicImg or ilPolyadicImg” on page 228

•

“Deriving From ilSpatialImg” on page 234

•

“Deriving New Classes From ilWarpImg and ilWarp” on page 237

•

“Deriving From ilFMonadicImg or ilFDyadicImg” on page 238

•

“Deriving From ilFFiltImg” on page 241

The subclasses of ilOpImg handle the tasks of reading raw data from the cache and
writing processed data back to the cache; if you derive from these classes, you are
responsible for writing only the function that processes the data in a given input buffer
and writes it to a given output buffer. If you derive directly from ilOpImg, you need to
supply your own interface to the cache as well as your processing algorithm. Figure 6-2
shows the operator classes you are most likely to derive from.

215

Chapter 6: Extending ImageVision Library

ilColorImg
ilMonadicImg
ilLutImg
ilFFiltImg

ilArithLutImg

ilHistLutImg

ilFMonadicImg
ilFPolyadicImg

...

ilFDyadicImg
ilOpImg
ilWarpImg
ilConvImg
ilSpatialImg
ilSepConvImg

ilPolyadicImg

Figure 6-2

ilDyadicImg

ilOpImg and Its Subclasses for Deriving

Remember that when you derive from a class, you inherit all of its public and protected
data members and member functions. You also inherit members from its superclasses.
You should review the header file and the reference page for any class you plan to derive
from (as well as the header file and reference pages of its superclasses) to become familiar
with its data members and member functions. It is also a good idea to look at a few of its
subclasses to see what general tasks they perform and what functions they implement.
Finally, you might want to take a look at the selected IL source code that is provided
online in /usr/share/src/il/src.
The next section contains information that is useful whether you derive directly from
ilOpImg or from one of its subclasses. The sections that follow contain more detailed
information about deriving from each of ilOpImg’s subclasses shown in Figure 6-2.

216

Implementing an Image Processing Operator

Deriving From ilOpImg
A class derived from ilOpImg needs to implement these member functions:
•

The constructor, which creates the object, declares which data types and pixel
orders are valid for the output, and sets the working data type.

•

resetOp(), which adapts to any attributes that have been altered, such as changing
the input image

•

keepPrecision(), which maintains the data type of a returned value.

•

prepareRequest(), which queues the data accessed from the input image(s) for a
requested page of the operator. It also allocates the buffer(s) to hold the input image
data.

•

executeRequest() which performs the operator’s processing when the input data is
loaded. The result is placed directly in a page of the operator’s cache.

•

finishRequest() frees any resources allocated in prepareRequest(). This is separate
from executeRequest() so that aborted operations that have already done
prepareRequest() can clean up without bothering with the work done in
executeRequest().

•

Any public setParam() and getParam() parameter set or get functions provided to
control the operator’s algorithm.

You also need to implement a destructor if you allocate any memory or change state
within the constructor or any other function you implement. Example 6-1 shows a
typical header file for an ilOpImg subclass.
Example 6-1

Typical Header for a Class Derived From ilOpImg

#include 
class myOperator : public ilOpImg {
public:
myOperator(ilImage* img, float param1);
void setParam1(float val)
{ param1 = val; setAltered(); }
float getParam1()
{ resetCheck(); return param1; }
};
protected:
void resetOp();
ilStatus prepareRequest(ilMpCacheRequest *req);

217

Chapter 6: Extending ImageVision Library

ilStatus executeRequest(ilMpCacheRequest *req);
ilStatus finishRequest(ilMpCacheRequest *req);
private:
float param1;

The resetOp() function should be declared protected if other programmers are likely to
want to derive a class from the myOperator class.
The Constructor

The constructor takes a pointer to the source ilImage(s) and additional arguments as
needed to provide parameters to control the operator’s processing algorithm (for
example, param1). If you do use additional parameters, you might want to define
corresponding functions that allow the user to alter and retrieve the value of those
parameters (such as setParam1() and getParam1()). These functions should probably
take advantage of IL’s reset mechanism by calling setAltered() and resetCheck(),
respectively. (See “The reset() Function” on page 208 for more information about how
IL’s reset mechanism works.) Example 6-2 shows you what a simple constructor might
look like.
Example 6-2

Typical Constructor for a Class Derived From ilOpImg

myOperator::myOperator(ilImage* img=NULL, float param1=Param1Default)
{
setValidType(iflFloat|iflDouble);
setValidOrder(iflInterleaved|iflSequential|iflSeparate);
setWorkingType(iflDouble);
setNumInputs(1);
setInput(img);
setParam1(param1);
}

In this example, myOperator can produce output of either iflFloat or iflDouble data type;
the output has the same pixel ordering as the input image. Input image data that is of
type iflFloat is cast to iflDouble before it is processed; this is the meaning of an operator’s
working type. Some operators can handle multiple inputs, but the setNumInputs()
function is used here to limit myOperator to one input. The setInput() function sets the
input to be the ilImage passed in; this step chains myOperator to the input image. Finally,
param1’s value is initialized.

218

Implementing an Image Processing Operator

The setValidType(), setValidOrder(), and setWorkingType() functions are all defined as
protected in ilOpImg. They are discussed in more detail in ilOpImg’s reference page. The
ilImage class defines setNumInputs() (protected) and setInput().
The constructor should not contain any calculations that are based on the value of
arguments passed in, since these arguments might change. Most operators that require
arguments other than the input image in their constructors define functions for
dynamically changing the value of those arguments (like setParam1()). Such calculations
should be done in the resetOp() function described below. The resetOp() function is
declared in ilOpImg, but its implementation is left to derived operators. Note that when
any ilImage is created, it is considered “altered,” so resetOp() is always called before any
data is computed.
The resetOp() Function

Since resetOp() is guaranteed to be called before prepareRequest(), executeRequest(),
and finishRequest(), it can—and should—be used to calculate the values of variables
needed by these methods, particularly if those variables depend on arguments passed in
the operator’s constructor. The resetOp() function also needs to reset any image
attributes that change as a result of the image’s data being processed, so that the proper
attribute values can be propagated down an operator chain. As an example, imagine an
operator that defined the following variables (probably as protected) in its header file
(ilMonadicImg defines these variables):
iflXYZCint str;
iflXYZCint istr;
int bufferSize;
int cBuffSize;

// output (page) buffer strides
// input image strides
// size of input buffer in bytes
// number of channels in input buffer

As you might expect, these variables are used to determine the size of the internal buffer
needed for reading in the image’s data that is to be processed. This buffer is actually
allocated in prepareRequest(), but the values for these variables are calculated in
resetOp(), since they depend on the input image’s page size and data type attributes.
Example 6-3 illustrates this with ilMonadicImg’s implementation of resetOp(). (The
iflXYZCint struct holds four integers, one for each of an image’s dimensions; see
“Convenient Structures” on page 365 for more information.)

219

Chapter 6: Extending ImageVision Library

Example 6-3

The resetOp() Function of ilMonadicImg

ilMonadicImg::resetOp()
{
// make sure we have a valid input
ilImage* img = getInput();
if (img==NULL || getOrder() == iflSeparate && getCsize() != img->getCsize())
{ setStatus(ilStatusEncode(ilBADINPUT)); return; }
// make sure page size info is in sync with color model/number channels
checkColorModel();
// determine whether or not we can use lockPage on our input
int cps, icps;
iflXYZint pgSize, pgDel, ipgSize, ipgDel;
getPageSize(pgSize.x, pgSize.y, pgSize.z, cps);
getPageDelta(pgDel.x, pgDel.y, pgDel.z, cps);
img->getPageSize(ipgSize.x, ipgSize.y, ipgSize.z, icps);
img->getPageDelta(ipgDel.x, ipgDel.y, ipgDel.z, icps);
iflOrder inord = img->getOrder();
usesIstr = 0; // XXX not supported yet
useLock = !inPlace && (usesIstr || pgSize == ipgSize && pgDel == ipgDel) &&
(cps == icps || cps == size.c && icps == img->getCsize()) &&
img->getDataType() == wType &&
img->getOrientation() == orientation &&
(order == inord ||
usesIstr && (order == iflSeparate) == (inord == iflSeparate));
// get buffer strides
getStrides(str.x, str.y, str.z, str.c);
if (useLock)
img->getStrides(istr.x, istr.y, istr.z, istr.c);
else
img->getStrides(istr.x, istr.y, istr.z, istr.c,
pgSize.x, pgSize.y, pgSize.z, icps, getOrder());
}

As shown, the resetOp() function performs three tasks:

220

•

makes sure the input is valid

•

determines whether to use lockPage() or getTile()

•

computes the stride parameters used in most calcPage() implementations

Implementing an Image Processing Operator

The size of the internal buffer depends on the operator’s working data type, on its page
size, and on the input image’s channel stride. Note that for this operator, the input and
output buffers are the same size. (All the functions used in this example are described in
Chapter 2, “The ImageVision Library Foundation,” except for iflDataSize(), which is
described in the reference pages.) In this example, none of the image’s attributes change
as a result of this operator’s image processing algorithm. An example of an operator that
does change attributes is ilRotZoomImg, which changes the image’s size, unless the user
has explicitly specified a desired size:
if (!isSet(ilIPsize)) {
// calculate newXsize and newYsize
size.x = newXsize;
size.y = newYsize;
}

Notice that the attributes are set directly; the setSize() function is not used since it would
flag the size attribute as having been altered. You can use isDiff() to determine whether
any parameters changed as a result of propagation. This function takes a mask of
ilImgParam values and returns TRUE if any of the specified attributes changed.
The keepPrecision() Function

When keepPrecision() is enabled, the data type of an operator’s input is maintained. If
it is not enabled, the data type of the operator’s input is translated into the smallest
possible data type. If you disable this function, it is possible that non-integral operator
input, such as float, will be cast into an integral data type, such as char. For example, if
keepPrecision() is disabled, and the operator’s input is really float values in the range of
0.0 and 1.0, the operator will change the data type of the range to char.
keepPrecision() is enabled by default.
To determine whether or not an operator maintains non-integral data types, use the
isPrecisionKept() function.

Handling Image Processing
When deriving your own operator, it is important to follow the ilMpRequest procedure
for operating on images. If you are deriving an operator from some class other than
ilOpImg, such as the ilOpImg-derived classes ilMonadicImg, ilDyadicImg, or
ilPolyadicImg, you can use the calcPage() method defined in those classes to operate on
the requested pages in a buffer. If you derive an operator from ilOpImg, however, you

221

Chapter 6: Extending ImageVision Library

cannot use calcPage(), nor can you use the pre-3.0 version of the ilOpImg::getPage()
method. Instead, you must use the three-step process for operating on images
summarized by three virtual functions in ilOpImg: prepareRequest(),
executeRequest(), and finishRequest().
prepareRequest Phase

In your derived operator, you must override the prepareRequest() method so that it
•

allocates buffer space for the input image to the operator

•

asynchronously reads in image data into the buffer that will be processed by the
operator

There are two ways to read the image data into a buffer:
•

Use qLockPageSet() if the operator can use the data of the stored image directly.

•

Use qGetTile3D() or qGetSubTile3D() if the operator cannot use the data of the
stored image directly.

If the page size of the stored, input image matches that of the output image, and the
operator can use the data type of the stored, input image directly, you can use
qLockPageSet() to directly fill the buffer. qLockPageSet() returns a pointer to the page
of the input image in cache. The advantage of using qLockPageSet() is that it avoids
copying the image data.
If you cannot use qLockPageSet(), you use the asynchronous methods qGetTile3D() or
qGetSubTile3D() to fill the buffer. These methods get a tile, change the data type,
allocate the buffer, and fill it.
Note: Do not use the synchronous versions of these methods, GetTile3D() and

GetSubTile3D() in the prepareRequest phase.
You only use qGetSubTile3D() if the page of image data to be operated on extends
beyond the boundary of the image, as shown in Figure 6-3.

222

Implementing an Image Processing Operator

Figure 6-3

Using qgetSubTile3D()

Each rectangle in the figure represents a page of image data. The pages on the right-side
border spill beyond the image boundary. Loading the part of the page that lies outside of
the image boundary is unnecessary and time consuming. Rather than loading the entire
page, you use qGetSubTile3D() to load only that portion of the page that lies within the
image boundary.
Whether you use qLockPageSet() or qGetSubTile3D() to read in the data, you pass them
the cache request mentioned in the argument of prepareRequest() as the parent, for
example:
ilMonadicImg::prepareRequest(ilMpCacheRequest* req)
...
ilMpMonadicRequest* r = (ilMpMonadicRequest*)req;
...
sts = im->qGetSubTile3D(r, r->x, r->y, r->z, r->nx, r->ny, r->nz,
r->in, r->x, r->y, r->z,
pageSize.x, pageSize.y, pageSize.z,
&cfg);

executeRequest Phase

You override the ilOpImg::executeRequest() method to perform the image operation on
the loaded image data. If, for example, you were writing a new sharpen operator, the
executeRequest() method would implement the sharpening of the image data.

223

Chapter 6: Extending ImageVision Library

finishRequest Phase

You override the ilOpImg::finishRequest() method to deallocate the buffer space used
by the image data and to unlock any pages locked (set by qLockPageSet()) in the
prepareRequest() method. You enter the finishRequest phase either because the
executeRequest() method completes or because the operation was aborted.
Image Processing Example
Example 6-4 shows what a request-processing implementation might look like under
this model.
Example 6-4

A Request-Processing Implementation for a Class Derived From ilOpImg

ilStatus
ilMonadicImg::prepareRequest(ilMpCacheRequest* req)
{
// do not proceed if things look bad
if (status != ilOKAY) return status;
// get the input image to read data from
ilImage* im = getInput(0);
assert(im != NULL);
ilMpMonadicRequest* r = (ilMpMonadicRequest*)req;
// queue request for the input data, either lockPage or getTile
ilStatus sts;
if (useLock) {
// doing lockPage, the page in the input image is the input buffer
r->lck.init(r->x, r->y, r->z, r->c);
sts = im->qLockPageSet(r, &r->lck);
}
else {
// doing getTile: if in place use our own page as destination, otherwise
//
allocate an input buffer
int nc = im->getCsize();
if (order == iflSeparate && nc == getCsize()) nc = getPageSizeC();
ilConfig cfg(wType, order, nc, NULL, r->c, getOrientation());
if (inPlace) r->in = r->getData();
sts = im->qGetSubTile3D(r, r->x, r->y, r->z, r->nx, r->ny, r->nz,
r->in, r->x, r->y, r->z,
pageSize.x, pageSize.y, pageSize.z, &cfg);
}

224

Implementing an Image Processing Operator

return sts;
}
ilStatus
ilMonadicImg::executeRequest(ilMpCacheRequest* req)
{
// do not proceed if things look bad
if (status != ilOKAY) return status;
ilMpMonadicRequest* r = (ilMpMonadicRequest*)req;
// find the input buffer,
void* src;
if (useLock) {
// doing lock page, input page is the input buffer
if (!r->lck.isLocked()) return r->lck.getStatus();
src = r->lck.getData();
}
else
// normal getTile, data was read into allocated buffer (or in place)
src = r->in;
// let the real operator code in derived class do it is thing
return calcPage(src, r->getData(), *r);
}
ilStatus
ilMonadicImg::finishRequest(ilMpCacheRequest* req)
{
ilMpMonadicRequest* r = (ilMpMonadicRequest*)req;
// free up any allocations or locks
if (r->in && !inPlace) {
// junk the input buffer
delete r->in;
}
else if (r->lck.getPage() != NULL) {
// unlock the page
ilImage* im = getInput(0);
assert(im != NULL);
im->unlockPageSet(&r->lck);
}

225

Chapter 6: Extending ImageVision Library

return ilOKAY;
}

The calcPage() function implements the image processing algorithm, taking care to
handle each valid data type appropriately. For example, Example 6-5 shows how
ilAddImg computes the pixelwise sum of two images.
Example 6-5

Computing the Pixelwise Sum of Two Images

#define doAdd(type) \
if (1) { \
type tb = type(bias); \
if (numIn == 2) { \
void *ib0 = ib[0], *ib1 = ib[1]; \
for (; idx < lim; idx += sx) \
((type*)ob)[idx] = ((type*)ib0)[idx]+((type*)ib1)[idx] +
tb; \
} else \
for (; idx < lim; idx += sx) { \
type sum = tb; \
for (int in=0; in < numIn; in++) \
sum += ((type*)ib[in])[idx]; \
((type*)ob)[idx] = sum; \
} \
} else

ilStatus
ilAddImg::calcPage(void** ib, int numIn, void* ob, ilMpCacheRequest&
req)
{
// for interleaved case: combine x/c loops to improve performance
int nc = req.nc, sc = str.c, nx = req.nx, sx = str.x;
if (sc == 1 && sx == nc) { nx *= nc; nc = 1; sx = 1; sc = 0; }
for (int z = 0; z < req.nz; z++) {
for (int y = 0; y < req.ny; y++) {
for (int c = 0; c < nc; c++) {
int idx = z*str.z + y*str.y + c*sc, lim = idx + nx*sx;
switch (dtype) {
case iflUChar:
doAdd(u_char); break;
case iflUShort: doAdd(u_short); break;
case iflShort:
doAdd(short); break;
case iflLong:
doAdd(long); break;
case iflFloat:
doAdd(float); break;

226

Implementing an Image Processing Operator

case iflDouble:

doAdd(double); break;

}
}
}
}
return ilOKAY;
}

Since ilAddImg is derived from ilPolyadicImg, this function uses ilPolyadicImg’s stride
data members—str.x, str.y, str.z, and str.c—to step through the data.
Because IL programs can be multi-threaded, the prepareRequest(), executeRequest(),
finishRequest(), and calcPage() functions should not alter any member variables or do
anything else that would make the algorithm non-reentrant. For example, the input
buffer used by prepareRequest() is allocated locally and stored as a member of the
request, rather than as a member in resetOp() so that concurrent execution of
prepareRequest() uses unique buffers for the different portions of the input image at the
same time.
Clamping Processed Data

Some operators might trigger overflow or underflow conditions as they process data. To
solve this potential problem, you should set clamp values that will then be used
automatically when overflow or underflow arises, as described below.
In your implementation of resetOp(), call setClamp():
void setClamp(iflDataType type = numilTypes);
void setClamp(double min, double max);

This function sets the values that pixels will be clamped to if underflow or overflow
occurs. The first version sets the clamp values to be the minimum and maximum values
allowed for the data type type; the default value of numilTypes means to use the
operator’s current data type. The second version allows you to specify actual clamp
values.
In the calcPage() function, use the initClamp() macro, passing in the operator’s data type
(for example, int or float). This macro initializes two temporary variables to hold the
minimum and maximum clamp values. Then, after you process each pixel of data, call
the clamp() macro and pass in the processed pixel value. This function clamps the pixel
value, if necessary, to the minimum or maximum clamp value.

227

Chapter 6: Extending ImageVision Library

To allow a user to set clamp values, you need to add ilIPclamp to the ilImgParam mask
passed to setAllowed() in the constructor.
Setting Minimum and Maximum Pixel Values

Another problem that might arise as a result of processing data is that the processed
values might exceed the range of values. For example, if you multiply two images (the
pixel values of which fall in the 0 to 255 range) and then display the result, you might end
up with pixel data that appears to be invalid if the pixel values exceed 255. To solve this
potential problem, operators that alter the data range of their inputs need to set the
minValue and maxValue data members (inherited from ilImage) to ensure that the
processed data can be displayed. When the data is displayed using ilDisplay, it is
automatically scaled between these values so that a meaningful display is produced.
Here is how ilAddImg computes minValue and maxValue in its resetOp() function
(ilAddImg performs pixelwise addition on two images; a user-specified bias value can
also be added to each pixel of the output):
// compute worst case min/max values
double min = getInputMin(0) + getInputMin(1);
double max = getInputMax(0) + getInputMax(1);
setStatus(checkMinMax(min+bias, max+bias));

The getInputMin() and getInputMax() functions return the minimum and maximum
pixel value attributes of the input image. The argument for these functions is the index
of the desired image in the list of inputs (the first input is at index 0). These values are
added (since that is what ilAddImg does), combined with the bias value, and then passed
to checkMinMax(). This function first attempts to set the operator’s data type to the
smallest supported data type that can hold the range specified by its arguments. If the
data type is explicitly set by the user, however, it will not be changed. Then, if minValue
and maxValue are not explicitly set, they are set to the values passed to checkMinMax().
If checkMinMax() returns ilUNSUPPORTED, it is not able to change the data type to
support the range; in this case, minValue and maxValue are set to the maximum range
of the current data type.

Deriving From ilMonadicImg or ilPolyadicImg
Both ilMonadicImg and ilPolyadicImg follow the getPage()/calcPage() model described
above. These two classes provide support for operators that take a single input image
(ilMonadicImg) or multiple input images (ilPolyadicImg) and operate on all pixels of the

228

Implementing an Image Processing Operator

input image data. Table 6-4 shows the classes that derive from ilMonadicImg and
ilPolyadicImg.
Table 6-4

Classes Derived from ilMonaDicImg and ilPolyadicImg

Classes That Derive from
ilMonadicImg

Classes That Derive from
ilPolyadicImg

ilAbsImg

ilAddImg

ilFalseColorImg

ilANDImg

ilFFiltImg

ilBlendImg

ilInvertImg

ilDivImg

ilNegImg

ilMaxImg

ilThreshImg

ilMinImg

ilColorImg (& subclasses)

ilMultiplyImg

ilLutImg (& subclasses)

ilORImg

ilScaleImg (& subclasses)

ilSubtractImg
ilXorImg

Here are some things you need to keep in mind if you derive from either of these classes:
•

Do not redefine prepareRequest(), executeRequest(), or finishRequest(); use the
version defined in ilMonadicImg or ilPolyadicImg. Just implement your algorithm
in calcPage().

•

If you redefine resetOp(), call the superclass version in your resetOp() (so that
buffers and page sizes are reset appropriately):
// either
ilMonadicImg::resetOp();
// or
ilPolyadicImg::resetOp();

•

Use setWorkingType() if you want the input buffer to be read in as a type different
from the operator image’s data type. Note that the output buffer always uses the
operator’s data type.

229

Chapter 6: Extending ImageVision Library

Example 6-5 shows that ilAddImg’s implementation of calcPage() takes three
arguments. Similarly, ilMonadicImg’s calcPage() function takes three arguments:
virtual ilStatus calcPage(void* inBuf, void* outBuf,
ilMpCacheRequest& req) = 0;

inBuf is the input buffer of data that needs to be processed, outBuf is the output buffer into
which the processed data should be written, and req is the request that describes the page
of data being processed. Your implementation of calcPage() (for any class derived
directly or indirectly from ilMonadicImg) must accept this argument list.
Since ilPolyadicImg processes more than one input image at a time, its calcPage()
function supplies an array of input buffers. As above, your implementation of calcPage()
must accept this argument list:
virtual ilStatus calcPage(void* inBuf1, void* outBuf,
ilMpCacheRequest& req) = 0;

When you derive from a class, you inherit all of its public and protected data members
and member functions. All the public members for ilMonadicImg and ilPolyadicImg
have been discussed in previous sections. The protected member functions are resetOp(),
getPage(), and calcPage(). For reference purposes, here are ilMonadicImg’s protected
data members:
iflXYZCint str;
iflXYZCint istr;
int bufferSize;
int cBuffSize;

// output (page) buffer strides
// input image strides
// size of input buffer in bytes
// number of channels in input buffer

The protected data members defined in ilPolyadicImg are similar:
iflXYZCint str;
iflXYZCint istr1, istr2;

// output buffer strides
// input image strides

int buffSize1, buffSize2;
// size of input buffers in bytes
int cBuffSize1, cBuffSize2; // number of channels in input
// buffers

230

Implementing an Image Processing Operator

Deriving From ilArithLutImg

As an abstract class, ilArithLutImg defines how to use look-up tables when performing
arithmetic or radiometric operations. To derive from it, you implement your algorithm
in calcRow() rather than in calcPage():
void calcRow(iflDataType intype, void *inBuf, void *outBuf,
int sx, int lim, int idx);

The intype parameter indicates the input image’s data type. The next two arguments are
the input buffer of data that needs to be processed and the output buffer into which
processed data should be written. The next three arguments specify how to step through
the data: sx is the x stride of the output buffer, lim is the maximum x stride, and idx is the
starting index. The calcRow() function contains the algorithm for processing one row of
input data. For efficiency, you can use the defined macro doRow() to obtain the proper
data type and feed it to the macro doCalc(). (The doRow() macro is defined in
ilArithLutImg’s header file.) If you use these macros, your calcRow() definition would
be just a call to doRow():
ilMyOpImg::calcRow(iflDataType inType, void* inBuf,
void* outBuf,int sx, int lim, int idx)
{ doRow(); }

and you would actually implement the computation algorithm in the macro doCalc(), as
ilPowerImg does, for example, as shown in Example 6-6.
Example 6-6

Implementation of ilArithDoCalc() in ilPowerImg

#define ilArithDoCalc(outype, intype) \
if (1) { \
if (inType == iflDouble || dtype == iflDouble) { \
for (; x < lim; x += sx) \
((outype*)outBuf)[x] = \
(outype)pow((double)((intype*)inBuf)[x]*scale+bias,
power); \
} \
else { \
for (; x < lim; x += sx) \
((outype*)outBuf)[x] = \
(outype)powf((double)((intype*)inBuf)[x]*scale+bias,
power); \
} \
} else

231

Chapter 6: Extending ImageVision Library

You also need to implement loadLut() to compute and load the appropriate values into
the LUT. Example 6-7 shows ilPowerImg’s version of loadLut().
Example 6-7

Implementation of loadLut() in ilPowerImg

void
ilPowerImg::loadLut()
{
double low, high;
lut->getDomain(low,high);
double dstep = lut->getDomainStep();
double lim = high+dstep/2;
for (double i = low; i < lim; i += dstep)
lut->setVal(pow(i*scale + bias, power), i);
}

For your convenience, ilArithLutImg has functions for scaling and biasing the input data
before the LUT is applied:
void setScale(double scale);
double getScale();
void setBias(double bias);
double getBias();

Deriving From ilHistLutImg

The ilHistLutImg class provides support for operators that compute a look-up table from
the histogram of the source image and then apply this table to the source image. It
derives from ilArithLutImg and implements its own versions of calcPage(), calcRow(),
and loadLut(). The only pure virtual function in ilHistLutImg is calcBreakpoints(),
which all derived classes must implement:
virtual ilStatus calcBreakpoints(ilImage *src, ilImgStat *imgstat,
double **brPoints) = 0;

This function computes the breakpoints (brPoints) of a piecewise LUT. You can think of
it as a pointer to a two-dimensional array whose members can be accessed by
double val = brPoints[i][j] where:
i = 0,1,2,...,nc-1
j = 0,1,2,...,nbinsi
nc = number of channels in the source image
nbinsi = number of bins in the histogram of channel i

232

Implementing an Image Processing Operator

You can obtain the number of bins by using imgstat’s getNbins() function. The variable
val in the example shown above represents what the pixel intensity represented by the
jth bin of the histogram for channel i maps to. For example, to invert pixel intensities of
an image containing unsigned char data, you can use
brPoints[i][j] = 255-j;

All the members of brPoints need to be evaluated in calcBreakpoints(), using both the
source image and a pointer to its associated data as inputs. Derived classes do not need
to allocate and manage memory for brPoints, since ilHistImg does this for them. In
addition, ilHistImg provides convenience functions for setting the ilImgStat and ilRoi
objects:
void setImgStat(ilImgStat *imgstat);
void setRoi(ilRoi *roi, int xoffset=0, int yoffset=0);

If you implement resetOp() in a derived class, be sure to explicitly call ilHistLutImg’s
version of resetOp().
An example of a class derived from ilHistLutImg might be an operator called
ilPixelCountImg, which replaces each pixel intensity by the number of times it occurs in
that particular channel. Such an operator might be implemented as shown in
Example 6-8.
Example 6-8

A Class Derived From ilHistLutImg to Count Pixels

class ilPixelCountImg:public ilHistLutImg {
private:
ilStatus calcBreakpoints (ilImage *src,
ilImgStat *imgstat, double **brPoints);
public:
ilPixelCountImg(ilImage *src);
}
ilPixelCountImg::ilPixelCountImg(ilImage *src)
:ilHistLutImg(src)
{
}
ilStatus calcBreakpoints (ilImage *src, ilImgStat *imgstat,
double **brPoints)
{
if (src==NULL) return ilBADINPUT;
int nch=src->getNumChans();
for (int i=0; igetHist(i);
int nbins = imgstat->getNbins(i);
int total = imgstat->getTotal(i);
double max = src->getMaxValue(i);
for (int j=0; j TRUE; background/invalid -> FALSE)
7. scan pixels while foreground state remains the same
8. call update()
9. return TRUE;

245

Chapter 7

7. Optimizing Your Application

This chapter is intended for programmers who are somewhat familiar with IL and who
want to optimize their applications. This chapter has two major sections:
•

“Managing Memory Usage” on page 247 describes how to optimize the memory
usage of your application.

•

“Using Hardware Acceleration” on page 253 describes what operations can be
accelerated on different graphics hardware.

Managing Memory Usage
You can optimize the performance of your application by making knowledgable
decisions about the use of memory resources. Three areas in which you can optimize use
of memory are:
•

use of cache

•

page size

•

buffer size

The following sections describe these three areas in greater detail.

Optimizing Use of Cache
You can optimize the use of cache in your application in a number of ways. You can
change the size of the cache, control the automatic growth of cache that can occur if
multi-threading is turned on, set priority on an image in cache, and use tools to monitor
the use of cache.
Before reading further, you might want to refer to other parts of this manual that describe
caching. To learn about:
•

caching and paging, read “The Cache” on page 32.

247

Chapter 7: Optimizing Your Application

•

changing cache size using the functions ilSetMaxCacheSize() and
ilSetMaxCacheFraction(), read “Managing Cache” on page 35.

•

using the ilCompactCache() and ilFlushCache() functions to compact global cache
memory, read “Managing Cache” on page 35

Cache Size

This section describes how to determine the cache size that is most appropriate to your
application. Every class descended from ilMemCacheImg (including all the image
operators) needs memory for a cache, which holds pages of image data. By default, IL
cache size is 30% of the total user memory on the system. In some applications this is too
large, in others it is too small.
The optimum cache size for any particular IL program depends on the size of the images
that the program manipulates and on the type of operations it performs on the data.
If your application:
•

operates on small images, you can set the size of the cache to be the size of the
image, minimizing both memory and total processing needs.

•

operates on large images, you will need a larger cache. A program with a large
image cache improves performance because it saves the processing overhead
required to move data in and out of memory. However, if the cache is too large and
uses up main memory, you could potentially be swapping pages in and out of
virtual memory on your system, which degrades performance.

•

displays image data, its cache should be large enough to hold the displayed
window of data.

•

just produces a reduced resolution version of an image in another image file, you
can get by with a smaller cache.

Typically, the cache will not be able to hold everything needed for an operation. For these
cases, set the cache at least large enough to hold both:
•

one page of output data

•

the number of pages of input data required to produce that page

For example, suppose that you are copying an image with pages that are 128 pixels
square (these are the default page dimensions for FIT images) to an image that sets the
page width to match the width of the image (this is true for SGI RGB images). Further,

248

Managing Memory Usage

suppose that both images are 2K pixels wide and that the SGI image sets its page height
to 64 pixels. Figure 7-1 shows the two images and the pages contained in them. (This
figure is not drawn to scale.)
2K

128
64

128

FIT Image

Figure 7-1

Page

SGI Image

Varying Page Dimensions

To write a single 2 KB x 64 SGI page, you need data from all the FIT pages that span the
width of the image. Thus, in this example, set the cache size to (2 KB x 64 + 2 KB x 128) x
3 bytes (assuming that there are 3 channels and that the data type is iflChar). Add about
10% to this figure to allow for the size of page descriptors and other overhead. This
allows all needed pages to be held in the cache. If the cache is smaller than this, the data
can still be processed, but FIT pages are bumped out of the cache and then read back in
as successive SGI pages are written.
Effect of Multi-threading on Cache

The use of multi-threading can affect the size of cache in an application (see
“Multi-threading” on page 53). With multi-threading enabled, the cache can grow larger
than its preset limit if all the pages contained within it are locked down and another page
must be brought into the cache. This growth of cache prevents deadlock, but can cause
the application to use more memory than you wish. To prevent this behavior, do one of
the following:
•

reduce the number of threads (so that there are never more threads than pages in
the cache)

•

reduce the size of each page (so that there are enough pages in the cache for all the
threads)

•

increase the size of the cache (so that there is one page for each thread)

249

Chapter 7: Optimizing Your Application

For example, if there is room in the cache for only two of the operator’s pages but there
are four threads, the cache may be grown so that it contains four pages. If this is
unacceptable, either reduce the number of threads to two or reduce the size of a page by
half (so that the cache can contain twice as many, or four, pages). Multi-threaded
applications always need more memory to run efficiently; the best solution is to add
more memory to your system. If this is not possible, the next best solution is to reduce
the page size.
Cache Priority

As explained in “Priority” on page 36, the pages of an image that are brought into cache
as the result of an operation on the image are kept there until the cache becomes full.
When the cache is full, decisions must be made about which pages are kept in cache and
which are discarded and replaced by new pages.
IL attempts to optimize the use of cache. You can also affect the caching process by using
the setPriority() and lockPage() methods. It is helpful, when you are optimizing your use
of cache, to understand actions IL is also taking to accomplish this. IL considers these
factors as it manages the contents of cache:
•

time since the last reference to a page. Pages most recently referenced are least likely
to be overwritten.

•

number of references made to a page. Pages that are frequently referenced are least
likely to be overwritten.

•

the destination of a page. IL automatically raises the priority of a page request for
data that is directly displayed. This has the effect of caching data at the end of a
displayed chain.

Sometimes it makes sense to cache data at points other than at the end of a chain. The
reference counting used in the page replacement algorithm can help to accomplish this
caching, but in cases where explicit knowledge of the application is required, you can use
the setPriority() method of ilImage to set the priority of the image containing the
specified page. For instance, you may want to raise the priority of the file input to a long
chain to avoid rereading the input if the chain is expected to be altered.
You may also want to raise the priority of the input to an operator that is having its
parameters interactively modified, although again the reference counting built into IL
will tend to automatically increase the priority for you.

250

Managing Memory Usage

Monitoring the Cache

You can monitor image data cache usage in two ways:
•

by using the image tool ilMonitor. This provides an interactive means for you to
monitor the use of the cache. See “Image Tools” on page 276 for more information
about ilMonitor.

•

by setting the environment variable IL_MONITOR_CACHE to a value of 1. This
causes IL to print a message for each page loaded into the cache or deleted from the
cache. The message identifies the page location in its associated image and the class
and address of that image.

It is often important to know about the operator images (such as color converters) that
are automatically inserted by IL. You can use ilDumpChain() to print out a simple
description of an IL chain.
An example using this environment variable is shown below:
% setenv IL_MONITOR_CACHE 1
% imgview /usr/demos/data/images/weather.fit
Page (0,0,0,0) loading in Color(0x10034ec8)
Page (0,0,0,0) loading in FIT(0x1001d010)

This example shows that a color converter operator image has been used to cache the
data from the FIT image in frame-buffer format. It also shows the background view with
ilConstantImg as input that is automatically created by ilDisplay. You can use this
technique to identify cache thrashing if you suspect it is occurring. You can eliminate
such problems by one of the techniques described in the preceding sections.
For more challenging situations, you may want to use the setPagingCallback() method
in ilCacheImg. Refer to the ilCacheImg reference page for more details.
Note: Do not attempt to use setPagingCallback() and ilMonitor at the same time since

ilMonitor uses the setPagingCallback() mechanism.

Page Size
Image data is always cached in pages. A file image’s page dimensions match those used
to store the image on disk. By default, an operator’s page size is defined by its input
images. Certain operators override this default size, which can affect the caching of
images. Some images also let you set the size of the pages in the cache and the data type

251

Chapter 7: Optimizing Your Application

and ordering of the cached data. The data type and ordering affect how data is cached,
so if you change these attributes, you might also want to change the size of the cache.
Operators (ilOpImg objects) can set minimum pages sizes to increase efficiency.
ilSpatialImg, for example, sets the minimum page size to a multiple of the kernel size.
Optimum Page Size

Operators are usually the only images that allow you to set the page size. The ideal page
size depends on the particular application, but in general you want an image’s page size
to be as close as possible to that of whichever image it is being copied to or read from. If
the application involves roaming on a large image, however, the page size should be
relatively square. The functions that change page size are defined by ilImage and are
explained in “Page Size” on page 38.
Large pages use up more memory, which is a problem when the cache grows beyond its
limit and starts allocating extra pages to get around deadlock. See the previous section
for suggested solutions. Making pages too small, however, forces too much processing
overhead. A page should not be smaller than 32 x 32 pixels, and in general the total
number of bytes in a page should be between 16KB and 64KB. This range typically works
out to be 128 x 128 to 256 x 256 when measured in pixels. Some operators, such as the
frequency domain ones, are more efficient when the page size is a power of 2.
Maximizing Efficiency When Copying Pages

The copyTile() method is an efficient way to copy a tile of data from one ilImage to
another:
ilStatus copyTile(int x, int y, int nx, int ny,
ilImage* other, int ox, int oy,
int* chanList=NULL);

By default, the tile is copied to the calling image from the image pointed to by other. The
x and y arguments specify the origin of the tile in the destination image, and nx and ny
specify the size of the tile. The tile that is to be copied is located at (ox,oy) in the other
image. (If the tile is at the same location in both the source and destination images, then
x=ox and y=oy.) If the source and destination images have different orientations, the data
is transformed automatically as necessary.

252

Using Hardware Acceleration

Buffer Space
You may sometimes need a temporary buffer to work on image data. Using copyTile()
instead of getTile() or setTile() to transfer data between images eliminates the need for
temporary buffers, saving you memory. copyTile() is explained in “Accessing Image
Data” on page 40.
In addition to temporary buffers you may allocate to hold data, IL allocates buffers to
operate on data internally. The amount of buffer space that IL can allocate at any one time
depends on the number of threads running concurrently. If three threads are performing
image processing operations on three tiles, in general, three buffers of the necessary sizes
must be used. However, extra buffer space is not used if the operator in question is
locking down pages, transferring data from input cache to output cache, and operating
on the data “in-place.” Certain operators derived from ilMonadicImg do this. If you
derive a new operator from ilMonadicImg or any of its descendants, you might want to
ensure that your derived class operates on its data in-place by setting its inPlace member
variable in the constructor.

Using Hardware Acceleration
IL can accelerate some image processing sequences on SGI computers that result in a
displayed image (as opposed to sequences that result in a file). This section describes
which IL operations can be accelerated, the constraints on these operations, and the
underlying graphics resource required for these operations.

Using Accelerated Operators
This section describes the operators that can be accelerated for display and the related
OpenGL functions that are required to accomplish the operators.
Accelerating ilAddImg, ilBlendImg, ilMaxImg, ilMinImg, ilMultiplyImg, and
ilSubtractImg Operators

These operators use the OpenGL blend facility to arithmetically combine two or more
input images. The primary (zero-th) input is rendered first to the frame buffer. Then the
subsequent inputs are rendered to the same location with the appropriate OpenGL blend
function enabled to accomplish the operation.

253

Chapter 7: Optimizing Your Application

ilMultiplyImg can only be accelerated if both input min values are zero.
ilSubtractImg is accomplished by negating the secondary input. Only
constant-alpha-type blending can be accelerated.
In some cases, the operation cannot be accelerated if the input data ranges differ.
Accelerating ilAndImg, ilInverImg, and ilXorImg Operators

These operators use the OpenGL logic OP facility to logically combine two or more
images. They use multi-stage rendering operations similar to that of ilBlendImg.
ilInvertImg, however, is done in a single rendering operation.
Accelerating ilConvImg and ilSepConvImg Operators

Convolution operators use the OpenGL 2D convolution extension. To facilitate
acceleration, the kernel data must
•

be of type float

•

be of one of the following sizes: 3x3, 5x5, 7x7

•

have the origin in the center of the kernel

Using the ilFalseColorImg Operator

IL uses the OpenGL color matrix. The matrix size must be less than or equal to 4x4. The
bias must be zero. Some matrices with negative weights may not be accelerated because
they cannot be scaled correctly.
Using ilLutImg, ilHistLutImg, and ilThreshImg Operators

IL uses OpenGL color tables. OpenGL provides four color tables (see Table 3-3). The color
table that is used for a particular operator depends on the LUT input (see Composition).
LUTs can be up to 4K long.
Using ilScaleImg, ilHistScaleImg, and ilNegImg Operators

IL uses OpenGL pixel scale, bias, and clamping facilities. These facilities are also used to
normalize input data ranges to the intrinsic zero to one ranges and to compensate for
convolution kernel and colormetric affects on the operator value ranges.

254

Using Hardware Acceleration

Accelerating ilWarpImg Operators

IL uses OpenGL texture rendering. There are two cases, depending on the type of warp,
associated with operators:
•

affine or perspective warp

•

any other type of warp

The first case sets the modelview matrix to perform the desired warp. The second case
represents the warp with a regular triangular mesh. For certain simple zooms, for
example, affine and perspective warp, the OpenGL pixel zoom facility is used instead of
texture.
The texture required for other warp cases is associated with the input of the warp
operator. Thus, multiple warp operators that share input also share the same texture. See
for more information about IL’s use of texture.
Accelerating the ilImgStat Operator

IL uses OpenGL’s histogram and minmax facility. The number of histogram bins must be
less than or equal to 4096. The input data order must be interleaved. An ImgStat with a
rectangular ROI can be accelerated, but one with any other kind of ROI cannot.
Note: ilHistLutImg and ilHistScaleImg use ilImgStat. Therefore, they accelerate

statistics-gathering and the rendering parts of the operations.

Understanding the OpenGL Imaging Pipeline
The OpenGL Imaging Extension (OIE) specifies a sequence of image processing
operations that can be enabled during a pixel transfer operation. A pixel-transfer
operation can be one of the following:
•

an image is drawn from the host memory to the frame buffer

•

an image is copied from one frame buffer to another

•

an image is loaded from the host to texture

•

an image is copied from the frame buffer to texture

In each case, a rectangle of pixels is transferred from one buffer to another. During the
transfer, any of the image processing operations shown in Figure 7-2 can be active.

255

Chapter 7: Optimizing Your Application

S/B/C1
Lut1
Conv
S/B/C2
Lut2
Color matrix1
S/B/C3
Lut3
Input buffer

Output buffer

Figure 7-2

OpenGL Image Processing Pipeline

In Figure 7-2, the input can be the host memory or a GL buffer, the output can be a GL
buffer, texture, or host memory, and S/B/C stands for scale/bias/clamp operators. To
use hardware acceleration, the operators must follow the order in Figure 7-2. Not all of
the operators need to be enabled. What is not allowed, for example, is Lut1 to precede
S/B/C1. If you need to use operators out of order, you need to use pixel buffers, as
described in “Pixel Buffers and Multi-Pass Acceleration” on page 258.
Most of the accelerated IL operators use one or more elements in the Image Processing
pipeline.

Composing Operators
Since the OGLIP supports a sequence of operations in a single operation, it is possible to
compose several IL operators for acceleration, provided they occur in the right order, for
example, IL chain shown in Figure 7-3 can be displayed by copying the file image cache
directly to the frame buffer while enabling the subsection of the OGLIP pipeline shown
in Figure 7-4.

256

Using Hardware Acceleration

ilSqRootImg
ilSharpenImg
ilSaturateImg

ilFileImg

ilView

Figure 7-3

IL Chain Mapped to the OGLIP Pipeline

Figure 7-4 shows that all three operators are accelerated. ilSqRootImg, ilSharpenImg, and
ilSaturateImg correspond to Lut1, Conv, and Color Matrix, respectively.
Lut1
Conv
Color matrix

Host
memory

Frame buffer

Figure 7-4

Mapping onto the OGLIP in a Single Transfer

However, if the chain is reordered, as shown in Figure 7-5, so that the sharpen occurs
after the FalseColor, the sequence cannot be fully accelerated because it does not match
the sequence of operators in the OGLIP pipeline. When a sequence cannot be wholly
mapped to the OGLIP, IL selects the longest subsequence to run as a single operator.

257

Chapter 7: Optimizing Your Application

ilSqRootImg
ilSaturateImg
ilSharpenImg

ilFileImg

ilView

Figure 7-5

Running a Subsection of an IL Chain

Given the IL chain shown in Figure 7-5, only the sharpen operator would be accelerated
with a single-pixel transfer. The other two operators would be evaluated in the normal,
unaccelerated manner.
The next section describes how chains as shown in Figure 7-5 can be fully accelerated
through the use of pixel buffers.

Pixel Buffers and Multi-Pass Acceleration
OpenGL provides non-volatile, off-screen framebuffer memory, called pixel buffers, for
storing intermediate results. This feature enables IL to fully accelerate chains that do not
completely map onto the OGLIP as a single transfer operation. For example, the IL chain,
shown in Figure 7-6, is accelerated with the two-pass sequence of transfer operations.

258

Using Hardware Acceleration

Lut1
Color matrix
Pixel buffer
Convolve
Host
memory
glDrawPixels()

glCopyPixels()
Frame buffer

Figure 7-6

Two-Pass Transfer Operations

Pixel buffers are, in general, eight to twelve bits per component. The exact depth of the
pixel buffers can be determined by examining the attributes of the glx visual associated
with the pixel buffer. The command
% glxinfo -fbcinfo

prints a summary of the available visuals. The limited depth of the pixel buffers limits
the precision of the stored image data.
Pixel buffers are allocated by IL in units of the display size. The total number of allocated
pixel buffers can be limited either programmatically through calls to
ilSetNumPBuffers() and ilGetNumPBuffers(), or through the environment variable
IL_NUM_PBUFFERS.

Texture
IL employs the OpenGL texture facility to accelerate warp operators. From the
standpoint of hardware accelerators, a texture is an intermediate storage buffer similar
to a pixel buffer. However, the size of the texture is usually smaller and the component
depth is shallower. The component depth is dependent on the resampling mode for the
warp (for example, ilNearNb, ilBiLinear, and ilBiCubic) and the color model.

259

Chapter 7: Optimizing Your Application

A texture is associated with the input of a warp. If several warp operators share the same
input, they also share the same texture. The texture cache is unaffected if the warp is
interactively altered to enable fast, interactive displays of changing warps.
IL provides limited support for displaying a combination of warps in a single rendering
pass. Specifically, you can string together any number of perspective (ilPerspWarp) and
affine (ilAffineWarp) warps into a single step. This combination of warps is called a
transform matrix. Figure 7-7 shows an IL chain of operators.
ilFileImg

ilWarpImg
ilRotZoom
ilAffineWarp
ilPerspWarp

ilRotZoom
ilAffineWarp
ilPerspWarp

1

User
warp
n

ilView

Figure 7-7

Accelerating an IL Chain Using Texture

Figure 7-8 shows the underlying data path of the IL chain in Figure 7-7.

260

Using Hardware Acceleration

Texture
Triangle mesh
Transform matrix

ilFileImg

ilView

Figure 7-8

Data Path of the IL Chain in Figure 7-7

Figure 7-8 shows that IL associates a texture with the ilFileImg object and derives a
triangular mesh from the user-defined warp. All of the perspective warps, affines, and
rotzooms are combined into the transform matrix. When any of these warp values
change the images change accordingly, however, changing the transform matrix does not
change the cached values for the texture and the triangular mesh. By preserving these
cached values, the use of the transform matrix accelerates image processing.
When the input image is larger than the texture, the data must be paged into texture
according to what is currently being viewed. When the texture requirement for a
particular rendering operation greatly exceeds the texture capacity, performance
degrades. In this situation, rendering is limited by the rate that texture can be loaded into
the cache rather than by the rate that it can be rendered.
The triangular mesh associated with a general warp is also paged into memory so that
only the displayed portion of the warp is evaluated. The results are cached and reused in
subsequent rendering operations.
Seeing Evidence of Pixel Buffer Use

The difference between the pipelines in Figure 7-3 and Figure 7-5 is that Figure 7-5 uses
a pixel buffer as an intermediary to create the final image. By turning on the monitor, you
can view the use of the pixel buffer in each pipeline by using the imgtcl program, which
is included in the software distribution. To view the results of Figure 7-3 at each stage of
the pipeline, use the following commands:

261

Chapter 7: Optimizing Your Application

% setenv IL_MONITOR 2
% imgtcl
imgtcl> ilfileimgopen monkey /images/monkey.rgb
monkey
imgtcl> view monkey
imgtcl> ilSqRootImg sqroot monkey
sqroot
imgtcl> view sqroot
imgtcl> ilSharpenImg sharp sqroot
sharp
imgtcl> view sharp
imgtcl> ilSaturateImg sat sharp
sat
imgtcl> view sat

To view the results of the pipeline in Figure 7-5, reverse the ilSharpenImg and
ilSaturateImg commands, as follows:
% setenv IL_MONITOR 2
% imgtcl
imgtcl> ilfileimgopen monkey /images/monkey.rgb
monkey
imgtcl> view monkey
imgtcl> ilSqRootImg sqroot monkey
sqroot
imgtcl> view sqroot
imgtcl> ilSaturateImg sat sqroot
sat
imgtcl> view sat
imgtcl> ilSharpenImg sharp sat
sharp
imgtcl> view sharp

Because the first two operators are the same in the pipelines shown in Figure 7-3 and
Figure 7-5 their images and monitor displays are identical. Figure 7-9 and Figure 7-10,
however, show the differences in the images and monitor displays caused by reversing
the ilSaturateImg and ilSharpenImg operators.

262

Using Hardware Acceleration

Figure 7-9

Hardware Acceleration Without Using Pixel Buffers

263

Chapter 7: Optimizing Your Application

Qualitatively, you can see that changing the order of the operators introduces added
complexity in the monitor displays shown in Figure 7-10. More specifically, you can see
that the ilSharpenImg operator in Figure 7-10 introduces the use of a pixel buffer.

264

Using Hardware Acceleration

pixel buffer used

Figure 7-10

Hardware Acceleration Using Pixel Buffers

265

Chapter 7: Optimizing Your Application

Texture Allocation
The total number of bytes of texture is configurable. The installed amount is encoded in
the OpenGL renderer string. For more information, see the man page for glGetString.
Texture is divided into two logical banks. Bilinear and nearest neighbor textures are
allocated within a single bank. Therefore, the maximum texture size for bilinear and
nearest neighbor texture is half the installed amount. Bicubic texture allocation is split
across the two banks. Therefore the maximum size of a bicubic texture is the same as the
installed amount.
Whenever a warp image is accelerated with texture, an auxiliary texture is managed that
shadows the input to the warp image. The size of this texture must be a power of two. If
the image is larger than the maximum allocatable texture size, then the maximum size
texture is used and maintained as a wrap-around image cache.

Hardware-Specific Acceleration Restrictions
This section describes the limitations and operating parameters for hardware
acceleration on different platforms.

General Restrictions
The following restrictions apply to all platforms:

266

•

The default pbuffer depth is based on the available X Visuals on the system. The
depth is determined by selecting the greatest-order visual class, usually TrueColor,
that has at least 8 bits per component, and includes alpha planes. It is not possible to
copy from a pbuffer to a window (or another pbuffer) with a different visual class.

•

Convolution kernel sizes must be 3x3, 5x5, 7x7, separable or general, and of type
float. The edge mode must be ilPadSrc, ilPadDst, or ilNoPad.

•

The color matrix (for ilFalseColorImg and ilSaturateImg acceleration) has. at most, 4
x 4 entries. The bias vector for ilFalseColorImg must be all zeros and the matrix
should have equal gain for each output channel, that is, the sum of the positive
elements and the sum of the negative elements should be the same for each matrix
row.

•

The look-up table size must be limited to 4096 entries.

Hardware-Specific Acceleration Restrictions

•

The histogram size must be limited to 4096 entries.

•

Multiply is accelerated only if the minimum scale value of the input images is zero.

•

Add is not accelerated for images with greater than two inputs.

•

Statistics are accelerated only for interleaved data.

•

The amount of texture available depends on the amount of texture memory
available in the system and the texture data format. An application should use
ilHwConnection::getTexCapacity() to determine the amount of texture available on
any given platform.
For any platform, an application should get at least one pbuffer for any framebuffer
configuration unless another application has taken all of the pbuffers.

InfiniteReality
Currently, the maximum amount of texture memory is 64 MB. The number of pbuffers
depends on the number of installed Raster Manager (RM) boards and the depth (in bits)
of the pbuffer. At the default depth, 12 bits, a 1 RM system can allocate 2 pbuffers.
The maximum texture lookup table size is 4096 entries if the texture and lookup table
format is single component, 2048 if neither format is more than 2, 1024 otherwise. These
numbers assume another application has not already taken the pbuffers.

Reality Engine
Restrictions for the Reality Engine are the same as for the InfiniteReality, except for the
following conditions:
•

The maximum amount of texture memory is 16 MB.

•

The OpenGL imaging pipe is restricted. It contains none of the extended lookup
tables, except for the texture lookup table.

•

Color matrix cannot be concatenated onto a convolution.

•

No pixel transformations in the imaging pipe can be active when loading texture.
The same is true for statistics operations.

•

The maximum texture lookup table size is 256.

•

Textures (and display lists) cannot be shared across GLX contexts.

267

Chapter 7: Optimizing Your Application

•

InfiniteReality does not support bicubic texture resampling.

•

Cannot accelerate conversions from color palette data to RGB.

•

Cannot load texture from pbuffer safely. (The data cannot be swapped and may be
obliterated due to a contending process.) It is disabled by default. It can be enabled
by setting the hint, IL_TEXTURE_FROM_PBUFFER_OK_HINT.

Impact/High Impact
Restrictions for the Impact are the same as for the InfiniteReality, except for the following
conditions:
•

Max Impact has 1 MB of texture memory, High Impact has 1 MB of texture memory,
and Certain Impact has no texture memory.

•

The maximum texture lookup table size is 256.

•

Impact does not support bicubic texture resampling.

•

Impact cannot convolve if the input is float type.

•

Impact cannot convolve into texture.

•

Some framebuffer configurations of High Impact do not support pbuffers.

Indy/Indigo2
Only blend, logic op, and arbitrary zoom are supported on an Indy. On Indigo Entry, XS,
XZ, Elan, or Extreme, only blend, logic op, and integer zoom are supported.

Hardware Hints
The ilHwHint class provides a mechanism for setting hardware-specific attribute values.
You can use this mechanism with the hints provided in the ImageVision library or you
can create hints of your own.
You can set hints globally or on operators. Hints set on operators take precedence over
globally-set hints.

268

Hardware Hints

An example of a hint is IL_TEXTURE_FORCE_HINT, which is defined in IL. This hint is
used on warp objects to force the warp operator to use texture even when a pixel zoom
could be used.

Using IL-Recognized Hints
Because ilHwHint::setHwIntHint() is overloaded, you can set a hint using either a hint
name or ID. Using a hint name causes a hit in performance because it requires a lookup
of the hint name. It is more convenient, however, to set a hint using its name. If you set a
hint repeatedly, use the hint ID to set it. If you set a hint only once, use the hint name to
set it.
Using the Hint ID

Because hint IDs are set at runtime, your application must first lookup a hint ID, save the
ID so that it does not have to be looked up again, and set the hint using the hint ID.
Example 7-1 shows this procedure.
Example 7-1

Using the Hint Name to Set a Hint

int texSizeIntHintID;
// Find and save the hint ID using its name.
texSizeIntHintID = ilHwFindHintID(“IL_TEXTURE_SIZE_HINT”);
// Create a RotZoom object to rotate the image by 30 degrees
ilRotZoomIMg rz(input, 30, 1, 1, BiLinear);
// Set the hint on the RotZoom object to limit the texture size
// to 1 million texels.
input->ilImage::setHwIntHint(texSizeHintID, 1024*1024);

Using the Hint Name

Instead of going through the trouble of finding the runtime hint ID and then setting the
hint based on it, you can just set the hint using the hint name, as follows:
input->setHwIntHint(IL_TEXTURE_SIZE_HINT, 1024*1024);

The trade-off for this easier construction is slower performance.

269

Chapter 7: Optimizing Your Application

IL-Recognized Hints

Table 7-1 describes the ilHwHint values currently recognized by IL.
Table 7-1

ilHwHint Definitions

Name

Description

IL_TEXTURE_SIZE_HINT

Uses int-valued hint to control the size in texels of
a warp operator input texture.

IL_TEXTURE_COMPONENT_SIZE_HINT

Uses int-valued component size hint to control the
warp operator input texture.

IL_TEXTURE_MESH_STEP_HINT

Uses int-valued hint to control the warp mesh step
size.

IL_TEXTURE_MESH_PAGE_X_HINT

Controls warp mesh page size (should be a power
of two).

IL_TEXTURE_MESH_PAGE_Y_HINT
IL_PIXEL_BUFFER_WIDTH_HINT
IL_PIXEL_BUFFER_HEIGHT_HINT

270

Uses int-valued hints to control pixel buffer
dimensions.

IL_TEXTURE_FROM_PBUFFER_OK_HINT

Uses int-valued hint to enable sourcing texture
from pixel buffer on RE (off by default because
correct texture swapping is not guaranteed).

IL_TEXTURE_LIMIT_HINT

Uses int-valued limit in bytes to control global
texture allocation for an application.

IL_TEXTURE_FORCE_HINT

Makes a warp object use texture, even if it could
use pixel zoom.

IL_FORCE_PASS_HINT

Disables composing with input pass; effectively
creates a multipass operation with pixel buffer
cache between them.

IL_ROAM_VIEW_HINT

Chains a texture onto an image when displayed in
an ilView; allows for fast roaming over an image.

IL_HW_HINT_ALERT_PROP_NAME

Marks an ilLink-derived object as altered when a
parent’s hints are changed or when the global
hints are changed.

Hardware Hints

Creating Your Own Hints
If you are implementing your own hardware acceleration, you may need to create your
own hints to control it. The ilHwHint class pairs a name with a value. You use these
values to optimize the performance of your application. The base class provides the
means to set and get the name in the following methods:
ilHwHint(const char* hintName);
const char* getHintName();
ilHwHint(int hintID);
ilHwFindHintID(const char* hintName)

The base class does not. however, provide methods to set or maintain values associated
with hint objects; that job is left for ilHwHint-derived classes. For example, you could
provide access to some integer value associated with a derived hint-class object,
ilHwIntHint, as follows:
class ilHwIntHint : public ilHwHint {
public:
ilHwIntHint(int hintVal) : ilHwHint(“ilHwIntHint”)
{ val = hintVal; }
private:
int val;
};

Note: You can use the base class directly only if the hint object does not need to define
an explicit value. In some cases, the presence of a hint on a hint list is sufficient.

When you create a hint object, it is assigned an ID. The first object has an ID of zero and
the following objects have ID numbers of 1, 2, 3, and so on. You can retrieve the ID of a
hint using the following ilHwHint method:
int getHintID()

You can use hint IDs to accelerate hint lookups in a list of hints. To look up a name, use
the name of the hint or its ID, if the derived class caches it. Hint lists are described further
in “Hint Lists” on page 272.

271

Chapter 7: Optimizing Your Application

Hint Lists

ilHwHintList manages a hint list, which is an array of hints. Using hint lists can
dramatically improve performance by reducing lookup delays.
To add a hint to a hint list, use one of the following methods:
ilStatus setHint(ilHwHint* hint, int adopt = FALSE);
ilStatus setIntHint(int hintID, int val);
ilStatus setIntHint(const char* hintName, int val);

To return the hint specified by a name or ID, use one of the following methods:
const ilHwHint* getHint(int hintID) const;
const ilHwHint* getHint(const char* hintName) const
int getIntHint(int hintID, int& val) const;
int getIntHint(const char* hintName, int& val) const;

To remove a hint, specified by a name or ID, from a hint list, use one of the following
methods:
ilStatus removeHint(int hintID);
ilStatus removeHint(const char* hintName)
ilStatus removeHint(ilHwHint* hint)

272

Chapter 8

8. The Programming Environment

This chapter describes the programming environment available on Silicon Graphics
workstations. Special tools are also described that may help you in writing, compiling,
and debugging your IL program.
This chapter contains the following major sections:
•

“Compiling and Linking an IL Program” on page 273 describes what you need to
do to compile an IL program written in C++, C, or Fortran.

•

“Reading the Reference Pages” on page 275 explains how to read the class reference
pages. These reference pages do not follow the standard UNIX® reference page
format.

•

“Image Tools” on page 277 describes some image tools that were developed using
the IL.

•

“Online Source Code” on page 277 describes the IL-related code that is available
online.

•

“Environment Variables” on page 278 describes how to configure the global IL
environment.

Compiling and Linking an IL Program
The following sections show you how to compile and link IL programs written in C++
and C.

Programs Written in C++
To compile an IL program, in this example, sample.c++, use the following command line:
# cc -g sample.c++ -o sample -lil

273

Chapter 8: The Programming Environment

By default, the *.so libraries are used to link your programs, however, you must link to
the IL library itself.
In general, you should not link to the static, *.a, libraries unless you want to keep your
application in one complete binary. If you do choose to use the static libraries, use the
following command to compile your program:
# cc -g sample.c++ -o sample /usr/lib/libil.a
-lm -lGL -IX11

/usr/lib/libifl.a -lil

If you link to the static libraries, include the IL library, the GL shared library, the X
Window library, the math library, and the C++ library.
A Sample Makefile

Example 8-1 shows a sample Makefile for compiling IL programs.
Example 8-1

Makefile for a C++ Program

# Makefile for IL test programs
SHELL = /bin/sh
# If you want to debug,turn on the “-g” option.
FLAGS = -g
MAINS= sample.c++
OBJS = ${FILES:.c++=.o}
PROGS = ${MAINS:.c++=}
LIBS = -lil
.c++:
CC $(FLAGS) $< -o $@ $(LIBS)
.c++.o:
CC $(FLAGS) -c $<
clean:
rm -rf $(OBJS) $(PROGS)
rm -rf core

274

Reading the Reference Pages

Programs Written in C
Link your C program to the libcil.so library, the C version of the IL. For example, to
compile a C program called ctest.c, use this line:
# cc -g ctest.c -o carprot -lcil

IL is compatible with ANSI C. To use the older, pre-ANSI dialect, add –cckr to the
command line. Ignore any warnings generated during compilation.
Refer to the C reference page for more information about the C compilers.
A Sample Makefile

Example 8-2 shows a sample Makefile for compiling IL programs written in C.
Example 8-2

Makefile for a C Program

# A very simple Makefile for IL test programs
SHELL = /bin/sh
FLAGS = -g
CMAINS = csample.c
COBJS = ${CMAINS:.c=.o}
CPROGS = ${CMAINS:.c=}
CLIBS = -lcil
.c:
cc $(FLAGS) $< -o $@ $(CLIBS)
clean:
rm -rf $(COBJS)
rm -rf core
clobber: clean
rm $(CPROGS)

Reading the Reference Pages
IL reference pages look atypical because they are class reference pages. They are available
online by typing the following on the command line:
# man -d ClassName

275

Chapter 8: The Programming Environment

ClassName is the name of the IL or IFL class that you want to read about.
A printed version of the reference pages is available as an option; see the Introduction for
ordering information.
The C++ and C versions of the class reference pages share a similar format. The following
list describes the main sections of each reference page:
Name

The class name and a one-line description of the class.

Inherits From

A colon-separated list of superclasses, beginning with the base class.

Header File

The class’s header file.

Class Description
Describes how the class fits into the IL and how to use it. This section
briefly mentions the most important functions associated with the class.
The C++ version also contains information about deriving from the
class, if appropriate.
Class Member Function Summary
Lists the prototypes of the functions associated with the class. They are
grouped functionally with headings that indicate the general task they
perform. Functions that are protected are identified as such. This section
should be a synopsis of the class.
Function Descriptions
Describes what each function does and what its arguments mean.
Sometimes code examples are included. This section is arranged
alphabetically so that you can easily find the description of a particular
function of interest.
Inherited Member Functions
Contains an alphabetical list of the functions inherited from
superclasses.
See Also

Lists other reference pages of interest.

Notes (optional)
Contains special information about the class.

276

Image Tools

Image Tools
IL provides several useful utilities for displaying, copying, and manipulating images.
Since these image tools are based on IL, they support TIFF, SGI, PCD (Photo CD), PCDO,
TCL, PNG, GIF, and FIT file formats. These tools are installed in /usr/sbin and most of
them are documented in the IRIS Utilities User’s Guide. (They also have reference pages.)
imgcopy

Image Copy copies a specified region of an input image file to an output
image file. It can also be used to convert between IL-supported file
formats. See the imgcopy reference page.

imginfo

Image Info reports image information such as size, data type, color
model, and file format for any IL-supported file format. See the imginfo
reference page.

imgview

Image View allows you to display and manipulate any combination of
IL-supported image files. Images can be roamed, dragged, cropped, or
wiped separately or simultaneously. See the imgview reference page.

imgformats

Image Formats lists all the IL-compatible formats currently installed.

Online Source Code
To provide you with source code examples, IL installs several directories in
/usr/share/scr/il. They are as follows:
•

guide contains the whole-program examples presented in this guide. They are
provided so that you can compile and run them as you read the relevant discussion
in the guide.

•

apps contains sample IL applications, such as imgcopy and imgview. These
applications serve as examples of how to program with the IL and serve as possible
templates for developing new applications.

•

src contains IL source code that may use to derive your own classes. The directory
includes the source for several operators, including the ilViewer class. The
corresponding header files are in /usr/include/il or /usr/include/ifl.

•

tutorial contains a series of programs that build on one another. The first in the series
(ex0.c++) simply opens and displays an IL image file. The other programs use
various operators and display techniques.

277

Chapter 8: The Programming Environment

You can examine the README files in the various directories for more information on
each of the code examples. Also, each of the directories containing complete programs
has an appropriate Makefile. To compile any of the programs, type:
# make program_name

where program_name is the name of the file minus its .c++ suffix.

Environment Variables
You use environment variables to configure the global IL environment. Environment
variables configure such things as the file format, multi-processing, graphics hardware
acceleration, caching capabilities, and monitoring functions.
Table 8-1 provides a brief description of the environment variables with their default
values.
Table 8-1

278

Environment Variable Definitions

Environment Variable

Definition and Default Value

IFL_DATABASE

Specifies the file location where the
IFL-supported image file formats are defined;
default is ifl/src/ifl_database.

IL_ARENA_MAXUSERS

Specifies the maximum number of threads that
can share a multi-processing arena; default is
40.

IL_CACHE_FRACTION

Specifies the amount of user memory reserved
for the cache; default is .3 (30%).

IL_CACHE_SIZE

Specifies the size of the cache; default is
IL_CACHE_FRACTION.

IL_COMPUTE_THREADS

Specifies the number of threads generated;
default is the number of processors in the
system.

IL_DEBUG

Specifies the debug level; default is 0.

IL_HW_ACCELERATE

Specifies whether or not hardware is used to
accelerate image processing; default is all
enabled.

Environment Variables

Table 8-1 (continued)

Environment Variable Definitions

Environment Variable

Definition and Default Value

IL_HW_DISPLAY

Specifies the X display used by IL to obtain a
display connection which is then passed to
XOpenDisplay().

IL_HW_RENDERER

Overrides the return value of
glGetString(GL_RENDERER) which forces
IL to treat the display as a different type of
renderer.

IL_MONITOR

Specifies whether or not all monitors are on;
default is off. Monitors print messages when
specific events occur.

IL_MONITOR_CACHE

Specifies whether or not a log entry is
generated when the cache is used; default is
off.

IL_MONITOR_COMPACTION

Specifies whether or not a log entry is
generated when the cache is compacted;
default is off.

IL_MONITOR_RESET

Specifies whether or not a log entry is
generated when an operator resets; default is
off.

IL_MONITOR_LOCKS

Specifies whether or not a log entry is
generated each time a lock is created or
destroyed; default is off.

IL_MP_ARENA_SIZE

Specifies the size of the arena; default is
2 Mb.

IL_MP_LOCKS

Specifies whether or not concurrent access to
IL data structures is allowed for threads;
default is on.

IL_NUM_PBUFFERS

Specifies how many pbuffers to try to allocate;
default is 1. IL tries to get as many as can up to
this value.

IL_READ_THREADS

Specifies the number of read threads used per
processor to handle disk I/O; default is one.

279

Chapter 8: The Programming Environment

The following sections describe the uses of some of the environment variables.

Caching Configuration Issues
You can use the environment variable IL_CACHE_FRACTION to specify the size of the
IL image data cache. The default size is 30% of available user memory. For example, you
could set the cache size to 20% of available user memory by issuing the following
command prior to running an IL-based application:
% setenv IL_CACHE_FRACTION .2

Alternatively, you can use the environment variable IL_CACHE_SIZE to set the size of
the cache in bytes. For example, you could set the cache size to 4 million bytes by issuing
the following command prior to running an IL-based application:
% setenv IL_CACHE_SIZE 4000000

The IL_CACHE_SIZE variable takes precedence over IL_CACHE_FRACTION if both
are set.

Hardware-Acceleration Configuration Issues
You can use the environment variable IL_HW_ACCELERATE to override the default
behavior of using the graphics hardware to perform processing whenever possible. For
example, you can disable the hardware acceleration feature of IL by issuing the following
command prior to running an IL based application:
% setenv IL_HW_ACCELERATE 0

You might turn off hardware acceleration when debugging operators that are accelerated
by the hardware.

Hardware Display Configuration Issues
When you open a display using IL, you first use the X call, XOpenDisplay(), to return a
pointer to the display device. The return value is then passed into ilDisplay to open a
window to display an image. You can use IL_HW_DISPLAY to set the value of the
display which the return value of XOpenDisplay() points to.

280

Environment Variables

The rendering machine returned by glGetString(GL_RENDERER) is generally the
machine you are using. You can override the value returned by glGetString(), however,
by setting the IL_HW_RENDERER value. For example, you might be running on an
InfiniteReality but want to see what the display would be like on an Impact. In this case,
you would set IL_HW_RENDERER to Impact.
Caution: Make sure your machine supports the platform you are setting
IL_HW_RENDERER to.

Monitoring Control Issues
You can use the IL_MONITOR environment variable to turn on the IL Monitor. The IL
Monitor logs an entry wherever one of the following events occurs:
•

The cache is used.

•

The cache is compacted.

•

An operator is reset.

•

A lock is either created or destroyed.

If you want a less-complete level of monitoring is needed, or you need to capture a log
of the operations, you can use any combination of the following environment variables:
•

IL_MONITOR_CACHE

•

IL_MONITOR_COMPACTION

•

IL_MONITOR_RESET

•

IL_MONITOR_LOCKS

If you set IL_MONITOR_CACHE to 1, a log entry is generated each time the cache is
used, for example:
Page (0,0,0,0) loading in File(0x1000a858)
Page (0,32,0,0) loading in File(0x1000a858)
Page (253,29,0,0) loading in Nop(0x100c8108)

281

Chapter 8: The Programming Environment

If you set IL_MONITOR_COMPACTION to 1, a log entry is generated each time the
cache is compacted, for example:
Compaction
Compaction
Compaction
Compaction
Compaction
Compaction
Compaction
Compaction
Compaction

reclaimed
reclaimed
reclaimed
reclaimed
reclaimed
reclaimed
reclaimed
reclaimed
reclaimed

144K.
0K.
160K.
144K.
0K.
64K.
176K.
0K.
0K.

If you set IL_MONITOR_RESET to 1, a log entry is generated each time an operator is
reset, for example:
(brandt@chaos:tests) convrz
File(0x1000a858) initialized
Convolve(0x1000db98) initialized
Rotate/Zoom(0x1000e1a0) initialized
X Window(0x100813f0) initialized
PBuffer(0x10099668) initialized
X Window(0x100ad9a8) initialized
X Window(0x100ad9a8) altered
View(0x100a3218) initialized
X Window(0x100ad9a8) altered
OpenGL Hardware Pass(0x100c1088) initialized
OpenGL Hardware Pass(0x100c1870) initialized
OpenGL Hardware Pass(0x100c1870) altered
Pixel Cache(0x100c2058) initialized

If you set IL_MONITOR_LOCKS to 1, a log of lock creations and destructions is
generated. Additionally, at program exit, any remaining locks are displayed. For locks
that are created, a short message is printed with the name of the lock. The name consists
of the address of the lock optionally followed by a parenthetical comment describing
what the lock is used for, for example:
Created
Created
Created
Created
Created
Created

282

lock
lock
lock
lock
lock
lock

4001fa0
4002070
4002140
4002210
40022e0
40023b0

(ilMpNode cache)
(ilMpParkedGroup cache)
(ilMpPool cache)
(ilLink mutex)
(ilImage evalLock)
(ilLink mutex)

Environment Variables

For the destruction of locks as they exit, the name of the lock and its metering
information are displayed. The metering information measures such things as how many
attempts were made to acquire the lock, how many of those attempts were successful,
and how many times the software was forced to start spinning on the lock.

Multi-Threading Configuration Issues
You can use the environment variables IL_COMPUTE_THREADS and
IL_READ_THREADS to specify the number of compute threads and file read threads
used by each processor. By default, one compute thread is created for each processor on
the host system (including the user’s thread), and one read thread is created to perform
disk I/O in the background. For example, you can disable all multi-processing features
in IL by issuing the following commands prior to running an IL-based application:
% setenv IL_COMPUTE_THREADS 0
% setenv IL_READ_THREADS 0

You can set the size of the arena used to allocate spin-locks and semaphores for
multi-processing control with the IL_MP_ARENA_SIZE variable. You might set this
variable if, for example, you create a large number of objects derived from ilLink.
By default, IL allows up to forty threads to share multi-processing arenas. If you need
more, you can set the IL_ARENA_MAXUSERS environment variable to a larger value.

283

Appendix A

A. What is New in Version 3.1

This appendix describes the differences between versions 3.0 and 3.1 of the ImageVision
Library.

keepPrecision() Added to ilOpImg
The ilOpImg method, keepPrecision(), was introduced to the IL library to maintain the
data types of operator inputs. Before version 3.1, the data type of operator inputs
defaulted to the smallest type that covered its range, for example, 0.0 to 1.0 defaulted to
char. This default behavior caused problems for non-integer input values, for example,
the range 0.0 to 1.0 might default to char when, in fact, the values were float.
For more information about keepPrecision() see “The keepPrecision() Function” on
page 221.

Multiprocessing on Single CPU Machines Enabled
Before IL version 3.1, asynchronous requests made on single-CPU machines failed. In
version 3.1, asynchronous requests spawn multiple threads to process requests.

Additional Image Formats Supported
In addition to all of the image formats supported by IL version 3.0, version 3.1 supports
Alias/Wavefront, SoftImage (by Microsoft®), and YUV (by Abekas) image formats.
For more information about image formats, see “Supported IFL Image File Formats” on
page 66.

285

Appendix A: What is New in Version 3.1

ELT Performance Enhanced
The performance of the Electronic Light Table (ELT) has been enhanced dramatically. For
more information about the ELT, see Appendix G, “Using the Electronic Light Table.”

Choosing OpenGL or X Rendering
The ImageVision library supports OpenGL as well as X rendering. IL version 3.1 allows
you to choose between the two with the following ilConfigure methods:
ilStatus ilSetRenderingStyle(Display* dpy, ilRenderingStyle style)
ilRenderingStyle ilGetRenderingStyle(Display* dpy)

where ilRenderingStyle is one of the following enumerations: ilOpenGLRendering or
ilXRendering. By default, ilOpenGLRendering is chosen if available. ilXRendering is
used primarily for rendering to a remote machine that does not support OpenGL.
You use ilSetRenderingStyle() to request a rendering style explicitly. If
ilOpenGLRendering is requested but not supported on the X connection, a status of
ilUNSUPPORTED is returned, and ilXRendering is set.
ilGetRenderingStyle() returns the current style.

API Change for ilImgStat
The API was changed for ilImgStat so that it behaves much more like ilImage. For more
information, see “Generation of Statistical Data” on page 132.

286

Appendix B

B. What is New in Version 3.0

This appendix describes the differences between versions 2.5 and 3.0 of the ImageVision
Library. If you are new to ImageVision, you should skip this appendix. The changes
mentioned in this appendix are integrated into the remainder of this manual.
This appendix is split into the following sections:
•

“Overview of Changes in 3.0” describes the major changes in IL 3.0.

•

“Understanding the New Features” describes in detail all of the features added to IL
3.0.

•

“Understanding the Changes to the Existing Features” describes in detail all of the
enhancements made to 2.x features.

•

“Backwards Compatibility with IL 2.5” describes in detail the conversions between
IL 2.5 and 3.0.

•

“New Derivations for Classes” describes in detail the new class hierarchy structure.

Overview of Changes in 3.0
The major new features in IL 3.0 are:
•

Support for OpenGL with transparent use of X rendering when OpenGL is not
available.

•

Support for 64-bit address space.

•

Support for a configurable error reporting mechanism.

•

Support for a generalized callback mechanism.

The major changes to existing features are:
•

IRIS GL rendering is no longer supported.

287

Appendix B: What is New in Version 3.0

•

The Fortran API. is no longer supported. It is still possible, however, to use the C
API from Fortran.

•

File format support is now provided by the Image Format Library (IFL).

•

Some base functions of IL were moved to IFL with corresponding name changes.

•

Hardware acceleration is now user extensible.

•

Scalability to larger numbers of processors has been improved.

•

Asynchronous operations are now supported.

•

Long operations can be aborted.

•

Handling of image warping has been generalized.

The following sections provide more detail on all of the changes and additions made in
IL 3.0.

Understanding the New Features
This section describes the new features in IL version 3.0.

Support for OpenGL and Hardware Acceleration
IL 3.0 is built on top of OpenGL instead of IRIS GL. Applications that were using IRIS GL
must be ported to OpenGL because the two graphics libraries are not compatible.
Moving to OpenGL also means that IL 3.0 performs optimally on new graphics hardware
that has a native OpenGL implementation. On older platforms, such as RealityEngine,
there will be some degradation in performance.
IL 3.0 maintains its own, internal OpenGL context which minimizes interactions between
hardware-accelerated operations and user rendering to the same window.
ilGLDisplayImg, ilGLViewer, and ilGLXConfig are gone. Programs that used those
classes must be ported to OpenGL and the ilXWindowImg and ilViewer classes.
IL 3.0 automatically manages a hardware rendering thread. ilHwIsSGI,
ilHwThreadEnable, ilHwThreadSuspend, ilHwThreadResume are also gone.

288

Understanding the New Features

64-bit Address Space Support
size_t, defined in /usr/include/sys/types.h, is used in IL 3.0 to specify the size of an object in
memory. Depending on the maximum size that an object in the virtual memory, size_t can
be either unsigned 32 bit or 64 bit. The return types of all functions returning the size of
an object used to be int; now they return size_t.

Understanding New Classes
The following classes have been added to IL.
ilAffineWarp

ilAffineWarp is a subclass of ilWarp that extracts the first degree polynomial coordinate
transformations that were formally embedded in ilPolyWarpImg. ilRotZoomImg uses
this warp class to represent its transformations.
ilCallback

ilCallback is a new base class with two derived classes: ilFunctionCallback and
ilMethodCallback. The derived classes provide a standard way to encode new function
callbacks. The external interface uses derived classes from ilFunctionCallback and
ilMethodCallback.
ilFunctionCallback creates a callback to a function that takes an argument, userArg. This
argument is supplied when the callback is created and a second argument, callerArg, is
passed from the callsite of the callback (when doit() is called).
ilMethodCallback is similar to ilFunctionCallback. You use both to create a callback to a
method on an object of a specified class.
iflColormap

iflColormap is derived from iflLut. The only difference between the two is that
iflColormap cannot have a one-to-one mapping onto the domain of the LUT. You use
iflColormap to represent the colormap for image files that have an iflRGBPalette color
model. All methods getting or setting colormaps now pass an iflColormap object instead
of an ilLut.

289

Appendix B: What is New in Version 3.0

ilCompoundImg

ilCompoundImg is an abstract class that can be used to manage an IL subchain as if it
were a single object. This can be useful for encapsulating an IL subchain used repeatedly
in an application. You can use IL to create variable subchains since the parents and
children of ilCompoundImg are attached to ilCompoundImg itself rather than internal
subchains. For example, an ilCompoundImg could encapsulate two subchains which
share common output processing but process input differently.
ilELTImg

ilELTImg, together with ilDisplay, ilView, and ilStereoView, provide functions needed by
Electronic Light Table (ELT) applications. ELT applications provide real time
manipulation of images, compressed or not. ilELTImg is derived from ilCompoundImg
and manages an image chain consisting of dewarp, convolution, table look-up and
histogram. Image manipulation, such as roaming and wiping as well as multi-image
display and stereo display, is supported in ilDisplay.
ilFPolarImg

ilFPolarImg is a new base class for ilFMagImg and ilFPhaseImg that operate on fourier
domain images.
ilFPolyadicImg

ilFPolyadicImg is a new base class for ilFMonadicImg and ilFDyadicImg that work on
fourier domain images. ilFMonadicImg and ilFDyadicImg are now derived from
ilFPolyadicImg as special cases.
ilFrameBufferImg

ilFrameBufferImg, derived from ilImage, is the basis for all IL access to frame buffer
memory. IL maintains internal display and GL contexts to isolate its rendering from the
user's code.
ilMath

In ilMath.h, some useful mathematical templates for integer types are defined, for
example, ilMod, ilDiv, ilDivUp, ilRoundUp, and ilRoundDown.

290

Understanding the New Features

ilPolyWarp

ilPolyWarp is a subclass of ilWarp that extracts the seventh-degree polynomial
coordinate transformations that were formally embedded in ilPolyWarpImg.
ilPolyadicImg

ilPolyadicImg, derived from ilOpImg, is the base class for N-input operators. Many
formerly ilDyadicImg-derived operators, for example, ilAddImg, ilAndImg, ilBlendImg,
ilMaxImg, ilMinImg, ilMultiplyImg, ilOrImg, and ilXorImg, now allow multiple inputs.
ilDyadicImg is now derived from ilPolyadicImg as a special case.
ilTiePointList

ilTiePointList manages a set of tie points used by ilTiePointImg. It provides methods to
add, remove, and locate tie points.
ilTimeoutTimer

ilTimeoutTimer provides a simple and efficient means of implementing a timeout period
for a polling loop. The timer automatically adapts its internal time checking to avoid
excessive reads of the hardware timer. Because of this optimization, the timeout period
is within 10% of the actual period.
ilTimer

ilTimer provides an interface to the high-resolution interval timer. On most SGI machines
this has a resolution of 1 usec or better.
ilWarp

ilWarp provides an abstract class to one-, two-, and three- dimensional coordinate system
warps from, respectively, (u), (u, v), and (u, v, w) space to (x), (x, y), and (x, y, z) space.
The subclasses ilPolyWarp, ilAffineWarp, and ilPerspWarp implement particular
instances of the ilWarp abstraction. The ilWarp class hierarchy enables more convenient
control of ilWarpImg.

291

Appendix B: What is New in Version 3.0

Understanding the Changes to the Existing Features
This section explains how IL version 2.5 features have changed.

Multi-threading Architecture Changes
The MP architecture of IL has been completely redesigned for IL 3.0. This redesign
enables
•

better scalability to larger numbers of processors

•

better integration of a dedicated rendering thread for improved graphics
performance

•

long operations to be aborted

The old ilDispatcher and ilRequest classes are replaced by the new ilMpManager and
ilMpRequest classes. The ilMpSetMaxProcs parameter, spare, which specified the
number of spare I/O threads is gone. IL 3.0 now uses a dedicated read thread.

Asynchronous Operations
All of the tile access methods, including getTile3D, setTile3D, copyTile, now have
asynchrounous versions, for example, qGetTile3D, qSetTile3D, qCopyTile, respectively,
that can access a tile without waiting for an operation to complete.
The caller can add a queued operation as a dependent operation on some other
ilMpManager or ilMpRequest. In this way, image operators handle input data tile
requests when processing a request to compute a page.
In addition, queued operations can return an ilMpManager object. This process allows a
number of synchronization options:

292

•

you can add a completion callback to the operation

•

you can explictly wait for the operation to complete at a later time

•

an operation can be aborted before it completes

Understanding the Changes to the Existing Features

Deriving Image Operators

To support asynchronous operations and aborting, the getPage() virtual function in
ilOpImg has been replaced by three new virtual functions that break the processing of a
page into three phases: prepareRequest(), executeRequest() and finishRequest().
In the prepare phase, an operator allocates input buffers and fills them with image data.
In the execute phase, the operator processes the image. In the finish phase, any allocated
buffers or locked pages are freed. The finish phase is separate from the execute phase to
allow aborted requests that have been prepared, but not executed, to skip the processing
phase and free allocated resources.
These changes are only visible to operators derived directly from ilOpImg. Operators
derived from more typical classes, such as ilMonadicImg or ilSpatialImg, still use the
calcPage() API.
Image Format library

The image file format functionality has been separated into a library called Image Format
Library (IFL). This library takes care of all I/O image processing. IL uses IFL to perform
image I/O. IFL applications do not have to use IL, however, IFL also simplifies and
standardizes the process of adding support for new image file formats.
The external interface to the IFL is encapsulated in two classes: iflFile and iflFormat. Note
that IL users will normally continue to use ilFileImg directly; not the IFL API. Refer to the
IFL man pages and release notes for more information on using IFL directly.
File formats

New image file formats such as PPM (portable pixmap file format) and PNG (new public
domain replacement for GIF) are supported in IFL.
JPEG compression support in TIFF files had been added as has the ability to decompress
JFIF images at reduced resolution and increased speed.
IFL also supports GIF file format writing, which was not supported in IL 2.5.

293

Appendix B: What is New in Version 3.0

Renamed Types Now Defined in IFL

File format types, tile descriptors, list objects, vectors, and other generic types, such as
ilBitArray, ilColor, ilConfig, ilCoord, ilDataSize, ilDictionary, ilHashTable, ilList, ilLut,
ilMinMax, ilPixel, ilSize, ilSpace, ilTile, and ilTypes, shared by both IL and IFL are now
in IFL. Consequently, the class name prefixes have changed from “il” to “ifl”.
A complete list of the name changes can be found in “Automatic Class Name
Conversion” on page 308.

Changes to the Display Facility
The biggest change to the IL display facility is that OpenGL has replaced IrisGL. IL no
longer supports GL windows or GL events. Additional changes to the display facility
include the addition of callbacks, queued paint operations, and new border styles.
All support for GL windows and GL event handling has been removed. Now only
support for X windows and X event handling is provided. Since X rendering and
OpenGL rendering can be mixed in the same X window, IL no longer has a render mode.
If the visual supports OpenGL, rendering is done with OpenGL. Otherwise, X is used to
render the images.
ilDisplay, ilViewer (and ilXWindowImg) can now create a window for the user with an
appropriate visual for the server they are connected to. ilViewer has a new eventLoop()
method that runs the event loop to make coding a simple IL application as easy as
possible. Callbacks have been added to ilDisplay and to ilView. Each has a post-render
callback and ilView also has a border callback. Paint operations can now be queued
which is useful for enhancing GUI response in applications. To turn on this feature, call
ilDisplay::enableQueueing(True).
All methods to get and set color for backgrounds, errors, and borders now take a float
rgb triplet (normalized to a range of 0-1) instead of ilPixel.
ilDisplayImg, ilGLDisplayImg, and ilXDisplayImg have been replaced by
ilFrameBufferImg and ilXWindowImg. Users no longer need to tell IL to use X rendering
since both OpenGL and X do their rendering to X drawables. Instead, IL determines if
the visual supports OpenGL; if it does, IL uses it to display a window.

294

Understanding the Changes to the Existing Features

Error handling
ilError provides a standard interface to handle error processing, notification, and
recovery. In the new interface, unexpected or exceptional conditions are classified
according to severity:
•

MM_HALT

•

MM_ERROR

•

MM_WARNING

•

MM_INFO

These severity levels are defined in pfmt.h; MM_HALT is the most severe, MM_INFO is
the lease severe.
When an error condition is encountered, the default behavior, defined in
ilNaiveErrorHandler, is to print a message to stderr. If the severity is MM_ERROR or
MM_HALT, IL exits the program.
IL provides three error handling functions:
ilNaiveErrorHandler

Prints message to stderr and aborts the program if
the severity is MM_ERROR or MM_HALT.

ilRobustErrorHandler Prints message to stderr and aborts if severity is
MM_HALT.
ilSilentErrorHandler

Prints a message to stderr and aborts if severity is
MM_HALT. Otherwise, remains silent and
continue execution.

The behavior of the error handling functions can be overridden by using
ilSetErrorHandler to supply a user’s error handler. If such a handler is not supplied, one
of the standard error handling functions is selected according to the environment
variable, for example, if IL_SILENT is set, ilSilentErrorHandler is selected if IL_ROBUST
is set, ilRobustErrorHandler is selected, otherwise, ilNaiveErrorHandler is selected by
default.
The current global error handling function can be queried using ilGetErrorHandler.
IL single-threads calls to error handlers. If an error happens in a thread, the thread blocks
until no other errors are being handled in other threads. One benefit of the blocking is

295

Appendix B: What is New in Version 3.0

that an error handler can send a message to an error stream in multiple fprintf()
statements without fear that the pieces will be shuffled together with messages from
another error handler running in a different thread.
As in IL 2.X, the type ilStatus is overloaded to represent both a function return value and
an object’s state. However, in IL 3.0, rather than being an enumerated type with only a
few dozen possible values, ilStatus is now a 32-bit quantity composed of 3 components:
unsigned int mainStatus:12 -- il status code
unsigned int subDomain:4 -- domain of subStatus
unsigned int subStatus:16 -- subdomain status code

mainStatus encodes a value of enumerated type ilMainStatus, similar to the 2.51 ilStatus
enumerated type. subStatus encodes an elaboration of the main status from another
domain. In order to determine what subStatus means, one must examine subdomain to
see what subStatus contains, for example, UNIX error numbers’s or iflStatus values.
The status code 0, ilOKAY, is reserved. Callers can check the ilStatus value without
worrying about specific return values.
ilError.h contains the definition of ilStatus (32-bit unsigned int) and the enumerated types
ilMainStatus and ilSubDomain; they represent the possible values of the mainstatus and
subStatus fields, respectively. ilError.h also contains inline functions and macros to
manipulate the following composite ilStatus values:
ilStatusEncode,
ilGetMainStatus,
ilGetSubDomain,
ilGetSubStatus,
ilStatusToString, and
ilStatusFromIflStatus.

Polynomial Coordinate Structures
Most of the polynomial coordinate structures defined in ilPolyDef.h have been removed.
Specifically, declarations for polynomials of degree 2 through 6 no longer exist. In
addition, the cubic polynomial evaluation routines in ilVector.h (ilVG3Poly and
ilVG3Poly2D) no longer exist.

296

Understanding the Changes to the Existing Features

The coefficient structures for first- and seventh-degree polynomial structures, defined in
ilPolyDef.h, have been renamed, as shown in Table B-1.
Table B-1

New Names for Polynomial Structures

Old Name

New Name

ilCoeff1_2d

ilAffine2D

ilCoeff7

ilPolyCoeff1D

ilCoeff7_2d

ilPolyCoeff2D

ilCoeff_1d

ilPoly1D

ilCoeff_2d

ilPoly2D

Run-time Object-Type Query Macros
The declaration and implementation for run-time object-type inquiries are generalized to
support any class, not just classes derived from ilLink, as the names in Table B-2 suggest.
Table B-2

Run-time Object Inquiries

Old Name

New Name

ilDeclareDerivedClass

ilClassListDeclare

ilImplementDerivedClass

ilClassListImplementBase (or
ilClassListImplementDerived)

At run time, a statically-created class inheritance chain supports run-time object type
inquiries. To acquire this capability, put the ilClassListDeclare macro in the public section
of a class declaration derived from a base class and the ilClassListImplementBase (for all
base classes) or ilClassListImplementDerived (for all derived classes) macro in the
implementation file. In addition, the methods declared in ilClassListDeclare are of const
type. Therefore, it is possible to call these methods on a const object.

297

Appendix B: What is New in Version 3.0

Changes to Existing Classes
The following miscellaneous changes have been made to existing IL classes:
ilCacheImg

In ilCacheImg, all callback-related functionality has been changed to use the standard
ilCallback interface. Specifically, the following callback-related functionality has been
replaced: setPagingCallback, setPagingCallbackDefaultEnabled, getPagingCallback,
getPagingCallbackArg, getPagingCallbackDefaultEnabled, enablePagingCallback,
isPagingCallbackEnabled, and listResident. These callback-related routines are replaced
by addPagingCallback, removePagingCallback, hasPagingCallbacks, and
doPagingCallbacks. Either ilPagingMethodCallback or ilPagingFunctionCallback
(newly defined) can be used with ilCacheImg::addPagingCallback().
ilClassId

ilClassId has changed to a const pointer to the ilClassList structure. ilConfig is now a
derived class of iflConfig instead of a base class. The color model parameter in the
constructor is now gone.
ilDilateImg

In ilDilateImg, the biasValue parameter in the constructor is gone.
ilDisplay

ilDisplay uses an overloaded constructor to specify visual attributes and to automatically
create an X window. getStart() and setStart() have been renamed to getMouse() and
setMouse() because their values are updated by the current mouse position after one or
more display operations.
ilDisplay::getVisibleArea() and ilDisplay::setVisibleArea() were added to clip painting
for scrolled window support.
ilDisplay::mapXY() was added to handle from and to orientations. Note that
ilCoordSpace has been renamed to iflOrientation.

298

Understanding the Changes to the Existing Features

enableFrontRedraw() and isFrontRedrawEnabled() were added to allow redraws to the
front buffer in double buffer mode. This improves an application’s perceived
responsiveness.
setMode() and clrMode() were added to control the display mode used for adding new
ilViews. New ilViews inherit dispMode() which makes it easier to defer painting while
adding many new views.
Post-render callbacks were added which are called after all views have been rendered.
There are three callbacks: prepare, render, and finish. The prepare and finish callbacks are
called by an IL compute thread. The render callback is called by the IL render thread to
maximize rendering performance. The prepare and finish callbacks can be NULL.
You can set and query a roam rate limit using setRoamLimit() and getRoamLimit(). You
use the roam limit to smooth the roam motion. The limit sets the displacement, in pixels,
between consecutively-rendered frames. Since the displacement is constant, regardless
of the motion of the mouse, the roam speed is constant. For example, if the roam limit is
four pixels, consecutively-rendered frames will be separated by four pixels regardless of
how much or little the mouse moves.
ilDisplay no longer supports autoSwap. Instead, pass ilNoSwap in the mode argument
to disable swapping.
ilDisplay::getScreenNum() was removed.
ilErodeImg

In ilErodeImg, the biasVal parameter in the constructor is gone.
ilFDyadicImg

cmplxVectorCalc() now has an extra argument to indicate whether the vector includes a
dc value. getPage() is gone and its functionality is replaced by prepareRequest(),
executeRequest(), and finishRequest().
ilFFTOp

ilFFTOp is no longer a public class. Forward and inverse transform, ilRfftf and ilRffti, can
be done only using ilRFFTfImg and ilRFFTiImg.

299

Appendix B: What is New in Version 3.0

ilFFiltImg

getPage() has been replaced by prepareRequest(), executeRequest(), and
finishRequest().
ilFMonadicImg

getPage() has been replaced by prepareRequest(), executeRequest(), and
finishRequest().
ilFalseColorImg

In ilFalseColorImg, a NULL constructor was added.
ilFileImg

ilFileImg was the abstract base class for various file format subclasses, for example,
ilJFIFImg, ilPCDImg, ilPCDOImg, ilFITImg, ilGIFImg, ilSGIImg, and ilTIFFImg. Those
subclasses are now gone.
ilGenericImgFile and ilFileImg have been replaced by ilFileImg. ilFileImg provides a
standard interface for opening or creating image files of all format types. All file
format-specific implementations are hidden inside the IFL.
There are major changes in ilFileImg.
ilFsDitherer

ilFsDitherer generates an optimal colormap for a full-color image and performs a high
quality dithering to produce a color index image. The algorithm that generates this
colormap is based on Heckbert's median cut algorithm. The function takes a pointer to
the source image and to the number of colors you want in the color-index image. The
function returns an optimal colormap based on the distribution of pixel values in the
source image. The input image can be dithered using the Floyd-Steinberg algorithm. IL
now maintains a global dithering mode that can be set and queried using two new, utility
methods: ilSetDither() and ilGetDither(). So, when creating a color index image, one
can either set the dithering to ilNoDither for no dithering, ilFSDither for Floyd-Steinberg
dithering, or ilSGIDither for standard SGI dithering.
These functions and the ilDither enum are defined in ilConfigure.h.

300

Understanding the Changes to the Existing Features

ilGBlurImg

In ilGBlurImg, getBlur() was added and setBlurKernelSize() was removed.
lHistLutImg

In lHistLutImg, getImgStat() and getRoi() were added.
ilHistScaleImg

In ilHistScaleImg, getImgStat() and getRoi() were added.
ilImage

In ilImage, the following methods were added: hwAccelerate(), isAccelerated(), copy(),
q[Get/Set]SubTile3D(), qCopyTileCfg(), qDrawTile(), qFillTile[3D/RGB](),
q[Get/Set]Tile3D(), qLockPageSet(), drawTile(), [alloc/get/free]FillData(),
fillTileRGB(), getDimensions(), getCopyConverter(), getHwOp(), getHwPassTable(),
getLockTileSet(), getPageOrigin(), hwDefine(), isIntegral(),
[get/set]MaxColormapLevels(), and [is/set]Writable().
The methods map[To/From]Input() replace the methods eval[XY/UV]().
The new methods accept 3D arguments which enables tracking of 3D coordinate
transformations through a chain. Numerous overloaded versions of these methods,
including the old 2D versions, are also provided.
Methods with a different argument or return type, for example, clipTile(), take a new
parameter of type iflTile3Dint which embodies offsets and dimensions.
getColormap() returns cmap through a return instead of a reference parameter. getFill()
returns fillValue through a return instead of a reference parameter.
The following methods are obsolete: [get/set]CacheSize(),
[get/set]Cache[Window/WindowCopy](), copyConverted(), isDisplayImg(),
needColorConv(), operator<<(), seekTile[/3D](), and eval[XY/UV].
map[To/From]Source() now supports 3D coordinate transformations. Numerous
overloaded versions of this method, including the 2D one, are also provided.

301

Appendix B: What is New in Version 3.0

[map/isMirror]Space() has been replaced by [map/isMirror]Orientation().
minValue() and maxValue() changed from ilPixel to scalar double.
[get/set][Min/Max]Value no longer have the channel parameter which means that
minimum and maximum values are no longer maintained separately for each channel.
getPageSize() now uses a return of type size_t instead of int.
getPageSize() returns nx and ny through reference parameters.
copyTile[_/3D/Cfg]() methods no longer have the fromOther parameter. Operations are
now always from the “other" image to "this" image.
getStrides3D() is replaced by getStrides().
setPageSize() is overloaded to allow only x and y page sizes to be set (and individual
flags added to reflect this). setPageSizeZ() and setPageSizeC() were added.
[get/set]SubTile3D(), copyTileCfg(), fillTile3D(), lockTile3D(), and lockPageSet() are
no longer virtuals.
lockPageSet() takes a new parameter, perPageCb.
hwGetPass() replaces hwEval().d
getColorImg() no longer has the optional parameter, img.
ilImgStat

In ilImgStat, the isAccelerated() method was added.
ilIndexableList

ilIndexableList is now a stand-alone class instead of a derived class from ilList. The new
methods, get[Next/Prev](), were added.
ilLink

In ilLink, all callback related methods have been simplified to use the standard ilCallback
interface. Specifically, setResetCallback(), setResetCallbackDefaultEnabled(),

302

Understanding the Changes to the Existing Features

getResetCallback(), getResetCallbackArg(), getResetCallbackDefaultEnabled(),
enableResetCallback(), and isResetCallbackEnabled() have been replaced by
addResetCallback(), removeResetCallback(), and hasResetCallbacks().
The following functions were removed: get[Depth/Child]() and [add/remove]Parent().
ilDumpChain is now the dumpChain() method. The mpLock() method does not take the
parameter, wait, anymore. The default value for parameter, spins, has changed.
The runtime type query methods are now const.
iflLut

iflLut was formerly ilLut. iflLut now supports scale and bias on index which allows a
LUT to have a different length than its domain, for example, a LUT can have floating
point input values ranging from 0-1 and have 256 entries. [get/set]Val() now takes an
index as a double. A new constructor takes LUT length. You can now use
getDomainStep() to access sequential LUT entries.
ilLutImg

getLookUpTable() now returns a pointer to the LUT through a function return instead
of a reference parameter.
ilMemCacheImg

In ilMemCacheImg, the method getPageTime() was added which returns the average
time to compute a page in the cache. The listResident() method now takes ilCallback as
a parameter. lockPageSet() is now inherited from ilImage and has one extra parameter,
perPageCb. [prepare/execute/finish]Request() methods now replace getPage().
ilFileImg, derived from ilMemCacheImg, is the only class still using getPage().
ilMemoryImg

In ilMemoryImg, setAutoSync() was removed as was the parameter, autoSyncEnable, in
the constructor.

303

Appendix B: What is New in Version 3.0

iflName

A new method was added to iflName: setID().
ilOpImg

In ilOpImg, checkDataType() was removed. getValidTypes() and getValidOrders()
were added to query attribute values. The inherit() logic has been tuned to only
re-inherit when inputs change.
ilPage

In ilPage, the null constructor was removed. [get/set]PID() was added that gets or sets
the process id that computes this page.
ilPropSet

ilPropSet is a pure virtual class. Two subclasses of ilPropSet are provided, ilPropList and
ilPropTable, which implement the abstract class as a linked list and as a hash table,
respectively. ilPropList is now derived from iflList instead of ilIndexableList. Interface
changes involved are: virtual functions, iterInit() and iterNext(), are removed from
ilPropSet. To iterate through all of the elements in the set, use the methods provided in
either iflList or iflHashTable.
ilSepConvImg

The ilSepConvImg constructor takes two new parameters, zKernzel and zsize, to support
three dimensional kernel specifications. Two methods, setZkernel() and getZkernel(),
were added that support three dimensional kernels. set[X/Y/Z]kernel() takes a new,
optional parameter, kernSize.
ilSpatialImg

The constructor for ilSpatialImg no longer takes the parameters inputKernel and biasVal.
It does, however, take a new parameter, inImg.

304

Understanding the Changes to the Existing Features

ilSpinLock

The atomicCreate() method takes one new optional parameter, name. The type of lockp in
the same method is now declared differently. ilSpinLock objects now have names for
monitoring (turned on with IL_MONITOR_LOCKS). New methods were added,
including lock(), unlock(), cset(), getName(), monitoringLocks(), dumpLockStats(), and
dumpLocks(). set() and unset() no longer return values.
ilSubImg

ilSubImg now has a NULL constructor. ilSubImg is now derived from ilOpImg so its
result is cached in memory.
ilRoiImg

ilRoiImg is now derived from ilCombineImg so its result is cached in memory.
ilMergeImg

ilMergeImg is now derived from ilOpImg so its result is cached in memory.
ilTieWarpImg

ilTieWarpImg now uses the generic warp functionality of ilWarp that is built into
ilWarpImg. It is possible to specify the warp using tie points, providing the underlying
ilWarp class supports warp inference using tie points. Also, ilTieWarpImg uses
ilTiePointList internally to maintain tie points.
The moveTiePoint() method is obsolete because it is no longer possible to reference a tie
point by an index.
ilView
Three border callbacks and three post-render callbacks were added to ilView. The border
callbacks draw the view borders; the post-render callbacks are called after the view has
been rendered. The three border and post-render callbacks are defined as follows:
borderPrepareCB = prepare;
borderRenderCB = render;
borderFinishCB = finish;

305

Appendix B: What is New in Version 3.0

postPrepareCB = prepare;
postRenderCB = render;
postFinishCB = finish;

The prepare and finish callbacks are called by an IL compute thread. The render callback
is called by the IL render thread to maximize rendering performance. The border
callbacks as well as the post-render callbacks can be enabled or disabled.
Note: The prepare and finish callbacks may be NULL.

Several new border styles were added, including BdrSolidLines, BdrDashedLines,
BdrCornerHandles, and BdrMiddleHandles. Use setBorderStyle() to change the border
style.
getDel() is now protected instead of private. Its return value is overloaded to XYZ or XY
image position (now in floating point). getImgLoc(), a new method, returns the location
of an image relative to its window.
ilViewer

Added ability to get/set X event window. This is used to support events that occur in
overlay drawables.
ilWarpImg

ilWarpImg has been substantially rearchitected. The addrGen() method is obsolete, and
there are no longer any pure virtuals in ilWarpImg. The warp is now specified by the
setWarp() method. The current warp can be accessed using getWarp(). Numerous other
internal changes were made to support the new MP methodology.

Backwards Compatibility with IL 2.5
IL 3.0 is not binary-compatible with earlier versions of the ImageVision Library (IL 2.5.1
and earlier). In addition, there are many source level differences. The largest difference is
the wholesale movement of core typing from IL to the IFL subsystem. IFL performs all
image reading and writing for IL. Consequently, all types dealing with image data types,
pixel channel ordering, color models, and coordinate systems have been moved into IFL.

306

Backwards Compatibility with IL 2.5

There is some IL 2.5 source-level compatibility available in IL 3.0, as described in
“Automatic Class Name Conversion” on page 308. You can turn on this compatibility by
compiling your source code with IL2_5_COMPAT defined. When IL2_5_COMPAT is
defined the following source level incompatibilities remain:
•

In IL 2.5, the ilConfig class contained an ilColorModel (now iflColorModel) field
named cm that is no longer present.

•

The old ilImage coordinate system member variable, space, has been renamed
orientation.
Note: IL2_5_COMPAT automatically changes the variable type from ilSpace to

iflOrientation.
•

The types ilXYS*, ilXYZS*, and ilXYZCS* are now defined in terms of the
corresponding IFL types which have constructors. As a result, using C style
initialization is not legal for these types. Code that uses such initialization must be
changed to use C++ style initialization, for example,
ilXYSint xy[2] = {{ 1, 2 }, {3, 4}};

becomes
ilXYSint xy[2] = {ilXYSint(1, 2), ilXYSint(3, 4)};

•

The global functions ilSetDefaultFileFormat() and ilGetDefaultFileFormat() are
not supported.

•

Transparent pixels are not supported by ilMedianCutCmapLut().

•

The ilList, ilListIter, and ilListIterRev classes (now the iflList, iflListIter, and
iflListIterRev classes) have become template classes. You must use them with a
template argument that declares what object types are linked into the lists, for
example, iflList.

•

ilStatus is no longer an enum. It now returns a major error code, a subsystem ID,
and a subsystem error code. As a result, any method which is declared to return an
ilStatus value must now return ilStatusEncode(), for example,
return ilStatusEncode(ilBADINPUT);

Note: There is a setStatus(int) method which automatically encodes the int

parameter and sets the ilImage object's status to that result.

307

Appendix B: What is New in Version 3.0

Automatic Class Name Conversion
When you define DIL2_5_COMPAT in your program, the IL compiler automatically
converts the 2.5 class names into their 3.0 equivalents, as shown in Table B-3.:
Table B-3

Class Name Conversions

Old Type Names

3.0 Type Names

ilBitArray

iflBitArray

ilColorModel

iflColorModel

ilMinWhite

iflNegative

ilMinBlack

iflLuminance

ilRGB

iflRGB

ilRGBPalette

iflRGBPalette

ilRGBA

iflRGBA

ilHSV

iflHSV

ilCMY

iflCMY

ilCMYK

iflCMYK

ilBGR

iflBGR

ilABGR

liflABGR

ilMultiSpectral

iflMultiSpectral

ilYCC

iflYCC

ilCompress

308

iflCompression

ilNoCompression

iflNoCompression

ilSGIRLE

iflSGIRLE

ilCCITTFAX3

iflCCITTFAX3

ilCCITTFAX4

iflCCITTFAX4

ilLZW

iflLZW

ilPACKBITS

iflPACKBITS

Backwards Compatibility with IL 2.5

Table B-3 (continued)

Class Name Conversions

Old Type Names

3.0 Type Names

ilConvIter

iflConvIter

ilConverter

iflConverter

ilCoordSpace

iflOrientation

ilUpperLeftOrigin

iflUpperLeftOrigin

ilUpperRightOrigin

iflUpperRightOrigin

ilLowerRightOrigin

iflLowerRightOrigin

ilLowerLeftOrigin

iflLowerLeftOrigin

ilLeftUpperOrigin

iflLeftUpperOrigin

ilRightUpperOrigin

iflRightUpperOrigin

ilRightLowerOrigin

iflRightLowerOrigin

ilLeftLowerOrigin

iflLeftLowerOrigin

ilDictionary

iflDictionary

ilFileFormat

iflDatabase

ilFillMode

iflFillMode

ilFillAll

iflFillAll

ilFillSome

iflFillSome

ilFillNone

iflFillNone

ilFlip

iflFlip

ilNoFlip

iflNoFlip

ilXFlip

iflXFlip

ilYFlip

iflYFlip

ilHashTable

iflHashTable

ilLinkItem

iflListItem

ilList

iflList

309

Appendix B: What is New in Version 3.0

Table B-3 (continued)

310

Class Name Conversions

Old Type Names

3.0 Type Names

ilListItem

iflListItem

ilListIter

iflListIter

ilListIterRev

iflListIterRev

ilLut

iflColormap

ilName

iflName

ilOrder

iflOrder

ilInterleaved

iflInterleaved

ilSequential

iflSequential

ilSeparate

iflSeparate

ilPixel

iflPixel

ilSize

iflSize

ilStackBuffer

use ilStackAlloc()

ilTile

iflTile3Dint

ilTileFloat

iflTile3Dfloat

ilType

iflDataType

ilBit

iflBit

ilUChar

iflUChar

ilChar

iflChar

ilUShort

iflUShort

ilShort

iflShort

ilULong

iflULong

ilLong

iflLong

ilFloat

iflFloat

ilDouble

iflDouble

Backwards Compatibility with IL 2.5

Table B-3 (continued)

Class Name Conversions

Old Type Names

3.0 Type Names

ilColorModelChans

iflColorModelChans

ilColorModelName

iflColorModelName

ilCompressionName

iflCompressionName

ilCoordSpaceName

iflOrientationName

ilCreateImgFile

use ilFileImg constructor

ilDataAnySign

iflDataAnySign

ilDataClosestType

iflDataClosestType

ilDataDemote

iflDataDemote

ilDataIsIntegral

iflDataIsIntegral

ilDataIsSigned

iflDataIsSigned

ilDataMax

iflDataMax

ilDataMin

iflDataMin

ilDataSize

iflDataSize

ilDataType

iflDataTypeFromRange

ilDataTypeName

iflDataTypeName

ilDataWantSigned

iflDataWantSigned

ilGetDefaultFileFormat

not supported

ilGetNextFileFormat

iflFormat::findNext()

ilGlobalDict

iflGlobalDict

ilGlobalName

iflGlobalName

ilMax

iflMax

ilMedianCutCmapLut

use ilFsDitherer class

ilMin

iflMin

ilOpenImgFile

use ilFileImg constructor

311

Appendix B: What is New in Version 3.0

Table B-3 (continued)

312

Class Name Conversions

Old Type Names

3.0 Type Names

ilOrderName

iflOrderName

ilSGICmapLut

use iflSGIColormap class

ilSetDefaultFileFormat

not supported

ilSpcGetTransform

iflOrientationTransform

ilSpcIsLeft

iflOrientationIsLeft

ilSpcIsLow

iflOrientationIsLow

ilSpcIsMirrorSpace

iflOrientationIsMirror

ilSpcIsTrans

iflOrientationIsTrans

ilSpcMapFlipTrans

iflMapFlipTrans

ilSpcMapSize

iflMapSize

ilSpcMapSpace

iflMapOrientation

ilSpcMapTile

iflMapTile

ilSpcMapXY

iflMapXY

ilSpcMapXYSign

iflMapXYSign

ilXYS

iflXYS

ilXY

iflXY

ilXYZS

iflXYZS

ilXYZ

iflXYZ

ilXYZCS

iflXYZCS

ilXYZC

iflXYZC

ilDot

iflDot

ilCross

iflCross

ilXY[char, int, float, double]

iflXY[char, int, float, double]

New Derivations for Classes

Table B-3 (continued)

Class Name Conversions

Old Type Names

3.0 Type Names

ilXYZ[char, int, float,
double]

iflXYZ[char, int, float, double]

ilXYZC[char, int, float,
double]

iflXYZC[char, int, float, double]

ilXYS[char, int, float, double] iflXYS[char, int, float, double]
ilXYZS[char, int, float,
double]

iflXYZS[char, int, float, double]

ilXYZCS[char, int, float,
double]

iflXYZCS[char, int, float,
double]

ilMultiListIterRev

iflMultiListIterRe

ilMultiList

iflMultiList

ilGenericList

iflGenericList

ilTile[2D, 3D, Float,2Dint,
2Dfloat, 3Dint, 3Dfloat,
2Dint, 2Dfloat, 3Dint,
3Dfloat]

iflTile2D[2D, 3D, Float,2Dint,
2Dfloat, 3Dint, 3Dfloat, 2Dint,
2Dfloat, 3Dint, 3Dfloat]

New Derivations for Classes
Because of the introduction of many new classes, the inheritance between classes has
changed, as shown in Table B-4.
Table B-4

New Class Hierarchies

Class

Old Base Class

New Base Class

ilAddImg

ilDyadicImg

ilPolyadicImg

ilAndImg

ilDyadicImg

ilPolyadicImg

ilBlendImg

ilDyadicImg

ilPolyadicImg

ilCombineImg

ilOpImg

ilDyadicImg

313

Appendix B: What is New in Version 3.0

Table B-4 (continued)

314

New Class Hierarchies

Class

Old Base Class

New Base Class

ilConfig

none

iflConfig

ilDyadicImg

ilOpImg

ilPolyadicImg

ilFDyadicImg

ilOpImg

ilFPolyadicImg

ilFFiltImg

ilOpImg

ilMonadicImg

ilFMagImg

ilOpImg

ilFPolarImg

ilFMonadicImg

ilOpImg

ilPolyadicImg

ilFPhaseImg

ilOpImg

ilFPolarImg

ilFSpectImg

ilOpImg

ilMonadicImg

ilIndexableList

ilList

None

ilLink

None

iflListItem

ilMaxImg

ilDyadicImg

ilPolyadicImg

ilMergeImg

ilImage

ilOpImg

ilMinImg

ilDyadicImg

ilPolyadicImg

ilMonadicImg

ilOpImg

ilPolyadicImg

ilMultiplyImg

ilDyadicImg

ilPolyadicImg

ilOrImg

ilDyadicImg

ilPolyadicImg

ilPropList

ilIndexableList

iflList

ilRoiImg

ilImage

ilCombineImg

ilSubImg

ilImage

ilOpImg

ilXorImg

ilDyadicImg

ilPolyadicImg

Appendix C

C. Introduction to C++

This chapter introduces the basic concepts of programming in C++. It briefly covers the
principal concepts that differentiate C++ from non-object-oriented languages. Rather
than providing a definitive overview, it gives C programmers a basic grasp of the C++
concepts and phrases that are occasionally used in this guide. If it has the side benefit of
piquing the interest of C programmers enough to give C++ a try, so much the better. One
primary benefit of programming in C++ is that you can extend the IL as you wish, for
example, to include support for your image file format or for an image processing
algorithm.

Objects and Classes
If you know that C++ is an object-oriented language, you correctly assume that objects
play a major role in a C++ program. An object is an instance of a C++ class. As a C
programmer, you are familiar with structures which provide a convenient grouping of
variables. A class is a fancy data type that defines not only data elements, as in a data
structure, but functions that manipulate those data elements. These data elements are
called the class’s data members, since they belong to the class; similarly, the functions that
manipulate the data members are called member functions.
One key member function is the constructor, which contains instructions about how to
create a class object. Typically, the constructor initializes the values of the data members.
The class destructor deallocates the class object. In C++, you can have the compiler
automatically create objects for you:
goodClass myGoodClass(anArg);

This statement defines the variable myGoodClass as being an instance of the class
goodClass; it invokes the goodClass constructor to create myGoodClass, passing in the
variable anArg as an argument to the constructor. Since storage is allocated for
myGoodClass, you can now invoke any of its member functions:
myGoodClass.doItNow(someArg, anotherArg);

315

Appendix C: Introduction to C++

This statement invokes the doItNow() member function, explicitly passing in two
arguments and implicitly passing the data elements of myGoodClass. Note the use of the
dot operator (“.”) to access the doItNow() member function of the goodClass. You can also
use this operator to access a data member of a class object, for example,
int defaultValue = myGoodClass.goodDefault;

where goodDefault is defined in the goodClass class.
Since the myGoodClass object is created automatically, it is also deleted (its storage freed)
automatically, that is, the class destructor is called automatically when the function goes
out of scope.
You can explicitly create an object as shown below:
goodClass* myGoodClassP = new goodClass(anArg);

Here, the goodClass constructor is explicitly called with anArg as the argument; note that
the constructor has the same name as the class and that it returns a pointer to the class
object. So, instead of a class object, you now have a pointer to a class. In this case, to access
one of its members, you have to use the arrow operator (“->”):
myGoodClass->doItNow(someArg, anotherArg);

Since you have explicitly created the myGoodClassP object, it is not automatically deleted.
You have to do this yourself:
delete myGoodClassP;

This statement calls the goodClass destructor to delete the object.

Overloaded Functions
A function in C can only be declared once. In C++, however, it is permissible to provide
more than one declaration of a function as long as the arguments in each function are
different. Since the function has more than one declaration, it is called overloaded.
Overloaded functions are used most commonly to declare class constructors. For
example, you might have the following constructors:
myClass();
myClass(int arg1, float arg2);
myClass(myType type);

316

Inheritance

The arguments that you pass into the constructor determine which version of the
constructor is used. You cannot, however, make the following declaration because the
arguments have the same form:
myClass(int serialNumber, float accuracy);
myClass(int imageNumber, float resolution);

Inheritance
Classes can inherit data members and member functions from other classes. Inherited
members are available for use by a class just as though they were defined in the class
itself. Inheritance occurs when one class is derived from another. The derived class
inherits the member functions and data from its parent, unless those members are
marked as private. (“Public versus Protected versus Private” on page 318 describes the
meaning of “private.”) Thus, classes exist in an inheritance hierarchy. As shown in the
inheritance hierarchy in Figure C-1, bestClass inherits from betterClass, which itself
inherits from goodClass.
goodClass

Figure C-1

betterClass

bestClass

Sample Inheritance Hierarchy

In this example, betterClass is “better” since it inherits members from goodClass and also
defines its own; similarly, bestClass inherits members from goodClass and betterClass,
and it defines its own. The root of a hierarchy is called the base class—in this example, the
base class is goodClass. Typically, the base class has several subclasses that derive from it;
it defines general capabilities common to every class in the hierarchy. A subclass then
adds definitions of whatever members it needs to implement in order to provide its
specific functionality.
A superclass can declare a member function as virtual, giving a subclass the opportunity
to provide its own definition of that function. In some cases, virtual functions are simply
declared but not implemented at all in a superclass. These are called pure virtual
functions, and they must be overridden by a subclass’s own version. You cannot create
an object of a class that contains pure virtual functions; such a class is called an abstract
class.

317

Appendix C: Introduction to C++

Public versus Protected versus Private
A class cannot use all of its superclass’s members. Some of a class’s members are declared
private, and they are available for use only by the member functions of that class. Other
members are declared protected, and these are available for use by derived classes. Yet
other members are declared public, and they are accessible anywhere in the program.

Passing by Reference
The C++ language allows variables to be passed by reference (as Fortran does). For
example, here is the declaration of a query function getAttribute(), which returns an
attribute’s value by reference:
void getAttribute(int& val);

Here is how you use this function:
int x;
myGoodClass.getAttribute(x);

It looks like getAttribute() is taking the variable itself, but behind the scenes, C++
actually passes a pointer to x.

Default Values
Another handy thing C++ allows you to do is to specify default values for a function’s
arguments. You do this when you declare the function:
void thisFunction(int arg1, int arg2 = 5);

Subsequently, you can call thisFunction() without explicitly specifying the second
argument:
myGoodClass.thisFunction(3);

This statement invokes the function, passing in 3 as the first argument and 5 as the
second. Additionally, you can specify whatever value you wish for the second argument
instead of relying on the default, as shown below:
myGoodClass.thisFunction(3, 7);

318

Class Declaration Format

Class Declaration Format
Example C-1 is a skeletal example of a class declaration to give you an idea of the
declaration format.
Example C-1

Class Declaration Format

#include 
#include 
class ilViewTile : ParentClass {
public:
float red, green, blue;
ilViewTile()
{ tile.x = tile.y = tile.nx = tile.ny = 0; mode = 0; }
ilViewTile(const iflTile2D& t, int m)
{ init(t, m); }
protected:
void qRender(ilMpNode* parent,
const iflTile2D& tile, int mode);
private:
void init(int mode);
void initSize(int mode);
};

In Example 1-1, ilViewTile class derives from ParentClass. The constructor for the class,
ilViewTile() is overloaded: it has two forms. The constructors are public functions.
The function qRender() is protected. The init() and initSize() functions are used
internally in the class and so are marked private.

Linking with Libraries in Other Languages
If you program in C++, you probably want to link with object files and libraries written
in languages other than C++, especially C. In order to do so, you must include in your

319

Appendix C: Introduction to C++

program declarations for the functions you wish to call. In most cases, you can do this by
including appropriate header files with the #include directive. For the standard C header
files supplied by Silicon Graphics, using #include is all you need to do. For example, if
you are going to use C standard I/O and the Graphics Library, write:
#include 
#include 

If you want to call C functions from within a C++ program, either directly or by file
inclusion, make sure that the C++ program contains correctly prototyped declarations
for the functions. Also, the function declarations need to be recognizable by the C++
translator as declaring functions whose definitions are in C.
These steps are necessary because C++ normally encodes function names to support
overloading. For example, the real name of a function declared in a C++ program as:
void printf(char*, ...)

is

printf__FPce.

The printf() function in libc.so, however, is called printf. To allow a C++ program to call
functions written in C, C++ provides linkage specifications. To use the standard printf()
function, for example, write:
extern "C" {
void printf(char *, ...);
}

within the C++ source file that calls printf(), or within a header file that is included by
the source file. The extern C statement tells the translator that the function linkage should
be done according to the conventions used by the C programming language.
If you want to adapt an existing C header file or create a header file of your own
containing C function declarations, and you want to be able to include it in either C or
C++ programs, you can use the symbol __cplusplus (with two underscores preceding it).
__cplusplus is always defined for C++ compilations and is otherwise undefined. Thus,
you can enclose C function declarations with:
#ifdef __cplusplus
extern "C" {
#endif

and
#ifdef __cplusplus
}
#endif

320

Referring to Function Names

This scheme is used to create the C and Fortran interfaces to the IL.

Referring to Function Names
The most important thing you need to know when debugging C++ programs with dbx is
how to refer to functions and data members:
•

Member functions. Refer to these as classname::functionName. For example, to set a
breakpoint in class C’s member function f(), type:
stop in C::f

If there is more than one member function named f(), this command will set a
breakpoint in every such function. (However, you cannot set a breakpoint in an
in-line function.)
•

Global C++ functions. Refer to these as ::functionname. For example, to set a
breakpoint in the global function f(), type:
stop in ::f

•

Non-C++ functions. Refer to these as functionname. For example, to set a breakpoint
in printf(), type:
stop in printf

•

Data members. You cannot refer to a data member by its name alone, even if the
program is stopped in a member function. To refer to data member m, use this–>m.

The following example illustrates various possibilities:
#include 
class foo {
int n;
public:
foo() {n = 0;}// this is an inline function
foo(int x);
int bar();
int bar(int);
};
int foo:: bar()
{
return n;
}

321

Appendix C: Introduction to C++

int foo:: bar(int x)
{
return n + x;
}
foo::foo(int x)
{
n = x;
}
int square(int x) // this is a global function
{
return x * x;
}
main()
{
foo a;
foo b = 11;
int x = a.bar();
int y = b.bar(x) + square(x);
printf("y = %d\n", y);
}

If you type:
stop in foo::foo

execution will stop in the constructor for the variable b but not in the constructor for the
variable a because you cannot set a breakpoint by name in an in-line function.
If you type:
stop in foo::bar

execution will stop both when a.bar is called and when b.bar is called because the
debugger is unable to distinguish between the overloaded functions.
To stop in square, type:
stop in ::square

To stop in printf (a C function), type:
stop in printf

322

Appendix D

D. Summary of All Classes

This appendix lists all the classes that make up the IL. Each of these classes has its own
reference page. Convenience functions that do not belong to any particular class are also
listed here. These functions have reference pages as well.
Table D-1

Summary of All Classes

Class or Function

Description

iflBitArray

Provides a limited, subscriptable bit array

iflClassList

Creates a class inheritance chain.

iflColor

Defines a few convenience functions for obtaining info
on color models.

iflColormap

Provides the base class for lookup tables.

iflConfig

Defines configuration of pixel data

iflConvIter

Provides an iterator for converters. It is used to step to
the beginning of each row shared in common between
two converters. The iterator also steps through the
channels.

iflConverter

Handles type conversion and reorganization between
arbitrary rectangular data buffers.

iflCoord

Contains structures to hold coordinates with
arithmetic operations.

iflDataSize()

Manipulates the size of IL data types

iflDatabase

Describes the capabilities of a particular image file
format.

iflDefs

Contains standard definitions required by the Image
Format Library.

iflDictionary

Implements a dictionary of named elements

323

Appendix D: Summary of All Classes

Table D-1 (continued)

324

Summary of All Classes

Class or Function

Description

iflError

Contains error codes, error handling, and assertion
macros used by the IFL library.

iflFile

Contains an abstraction of a handle to an image file.

iflFileConfig

Describes the configuration of an iflFile. It is used with
the iflFile::create() calls to query file configurations.

iflFormat

Describes the capabilities of a particular image file
format.

iflHashTable

Contains the base class from which hash table
implementations can be derived.

iflList

Contains the base class for a simple doubly-linked list.

iflLut

Defines a lookup table.

iflMinMax

Contains the functions for performing minimum and
maximum comparisons

iflOrientation

Transposes the orientation of an image’s axis.

iflPixel

Defines a pixel.

iflSGIColormap()

Contains the functions that create or access color
maps.

iflSize

Defines the size of an image.

iflTile

Defines a three-dimensional rectangle of image data.

iflTileIter

Cycles through the pages spanning a tile.

iflTypeNames

Provides a some convenience functions to get the
ASCII string for some of the enumerated types used
by IFL.

iflTypes

Defines the image data types, pixel component
ordering, supported Color Models, supported
orientations, supported compression schemes, and
flip modes.

ilABGRImg

Converts to the ABGR color model.

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilAbsImg

Computes the pixelwise absolute value of an image.

ilAddImg

Computes the pixelwise addition of two images.

ilAndImg

Computes the pixelwise logical AND of two images.

ilArena

Defines an area of CPU memory shared by multiple
threads.

ilArenaItem

Creates a shared memory version of your favorite
objects.

ilArenaSem

Provides an interface to the user mode semaphore
services.

ilArenaSmallBitArray

Provides a shared memory version of the
ilSmallBitArray class.

ilArenaSpin

Provides an interface to the user mode spin lock
services.

ilArithLutImg

Performs a generalized arithmetic operation using a
look-up table.

ilArrayAlloc

Allocates memory for arrays.

ilAtomicOps

Provides inline functions to define some useful atomic
operations. This header file is mainly intended to ease
portability of code using these operations.

ilBGRImg

Converts to the BGR color model.

ilBitMapRoi

Defines a bitmap-based region of interest (ROI).

ilBlendImg

Blends images.

ilBlurImg

Blurs an image.

ilBoundingBox

Accumulate 2D bounding box of a set of points.

ilBuffer

Provides a four-dimensional resizable buffer.

ilCMYKImg

Converts to the CMYK color model.

325

Appendix D: Summary of All Classes

Table D-1 (continued)

326

Summary of All Classes

Class or Function

Description

ilCache

Implements the new and delete operators to allow
objects of derived classes to be cached. This can be
used to minimize calls to malloc() and free() for
objects that are frequently constructed and destroyed.

ilCacheImg

Implements image data caching.

ilCallback

Implements callback method which is a abstraction of
a pointer-to-function parameter.

ilChromaKeyImg

Compares each input pixel against a statistical
measure of the “background”. If the pixel is close
enough to the mean value of the background it is
marked as being in the background.

ilColorConv

Converts between color models.

ilColorImg

Converts to the ABGR color model.

ilCombineImg

Combines two images controlled by an ROI.

ilCompassImg

Performs a directional gradient transform of an image.

ilConfigure

Contains routines to configure the IL environment.

ilConstImg

Defines a constant-valued image.

ilConvImg

Performs general image convolution.

ilConvPixel

Converts a pixel to different color models.

ilDilateImg

Performs morphological dilation on an image.

ilDisplay

Manages the display of images in an X window.

ilDisplayDefs

Defines various binary flags common to ilDisplay and
ilView. Many display operators take a mode
parameter that is the bitwise OR of one or more of
these flags (e.g. mode = ilCenter | ilDefer).

ilDisplayMgr

Handles cleanup of ilViewCbArg. Created for ilView
callbacks.

ilDivImg

Computes pixelwise division of two images

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilDyadicImg

Provides basic support for dual-input operators

ilELTImg

Implements the functions needed for Electronic Light
Table applications.

ilELTRoamer

Supports roaming look-ahead and zooming
look-ahead for ELT applications.

ilELTrset

Encapsulates all information related to an R-set.

ilEnviron

Provides support for environment variables.

ilErodeImg

Performs a morphological erosion on an image.

ilExpImg

Performs pixelwise exponentiation of an image.

ilFConjImg

Computes the conjugate of a Fourier image and
normalizes the complex value by a real factor.

ilFCrCorrImg

Computes the cross-correlation of two Fourier images.

ilFDivImg

Divides two Fourier images.

ilFDyadicImg

Provides basic support for dual-input Fourier
operators.

ilFExpFiltImg

Applies an exponential Fourier filter to a Fourier
image.

ilFFiltImg

Provides basic support for Fourier filter operators.

ilFFTOp

Performs a forward, inverse, or average fast Fourier
transform of an image.

ilFFiltImg

Provides the base class for frequency filters.

ilFGaussFiltImg

Applies a Gaussian Fourier filter to a Fourier image.

ilFileFormat

Registers supported image file formats.

ilFileImg

Provides basic support for image files.

ilFMagImg

Computes the magnitude values of a Fourier image.

ilFMergeImg

Merges magnitude and phase images into a Fourier
image.

327

Appendix D: Summary of All Classes

Table D-1 (continued)

328

Summary of All Classes

Class or Function

Description

ilFMonadicImg

Provides basic support for single-input Fourier
operators.

ilFMultImg

Multiplies two Fourier images.

ilFPhaseImg

Computes the phase values of a Fourier image.

ilFPolarImg

Provides the base class for single input fourier
operators.

ilFPolyadicImg

Provides the base class for multiple input fourier
operators.

ilFRaisePwrImg

Raises the magnitude values of a Fourier image by a
power.

ilFSpectImg

Computes the spectrum of a Fourier image.

ilFalseColorImg

Performs false coloring of multispectral images.

ilFrameBufferImg

Provides the basis for IL access to frame buffer
memory. IL maintains internal Display* and GL
contexts to isolate its rendering from the user’s code.

ilFsDitherer

Allocates and returns an optimized color map.

ilGBlurImg

Performs a two-dimensional Gaussian blur of an
image.

ilGrayImg

Converts to the gray-scale color model.

ilHistEqImg

Performs histogram equalization of an image.

ilHistLutImg

Provides the base class for operators that compute a
lookup table based on a histogram.

ilHistNormImg

Performs histogram normalization of an image.

ilHistScaleImg

Performs histogram scaling of an image.

ilHSVImg

Converts to the HSV color model.

ilHSVconverter

Converts HSV to RGB file format.

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilHwConnection

Provides private connections to the display server and
serves as a graphics hardware capability query
mechanism.

ilHwContext

Provides the foundation for all of IL’s rendering to
graphics hardware.

ilHwDefs

Defines some types and enums for hardware
acceleration.

ilHwManager

Provides the base class for various types of hardware
accelerated rendering. This header file includes a
couple of miscellaneous managers for fillTile and
qBarrier operators.

ilHwManagerGL

Implements those rendering operations that can be
accomplished with the OpenGL drawPixels function.

ilHwMgrELT

Provides the ilMpManager for Electronic Light Table
applications.

ilHwPass

Encodes the hardware acceleration for an ilImage (or
an ilImgStat).

ilHwPassELT

Implements hardware acceleration for the ELT
application.

ilHwPassGL

Implements hardware acceleration in OpenGL.

ilHwTexture

Represents a single GL texture.

ilImage

Provides basic support for images.

ilImgRoi

Defines the image-mapped Roi class.

ilImgStat

Computes the histogram, minimum, maximum,
mean, and standard deviation of an image.

ilIndexableList

Provides an indexable linked list.

ilIndexableStack

Manages an indexable list as a stack.

ilInvertImg

Performs one’s complement of an image.

ilKernel

Defines kernels.

329

Appendix D: Summary of All Classes

Table D-1 (continued)

330

Summary of All Classes

Class or Function

Description

ilLaplaceImg

Performs edge detection using Laplacian kernels.

ilLink

Provides chaining and setting attributes.

ilLinkIter

Provides an iterator for ilLink.

ilLockPageCache

Manages a toroidal cache of lock requests on an image.

ilLogImg

Computes the pixelwise logarithm of an image.

ilLutImg

Translates an image using a lookup table.

ilMachDep

Provides definitions and typedefs used to detect
machine dependencies and compensate for them to
ease the difficulty of porting the IL to other platforms.

ilMath

Facilitates the mod operation for integer types.

ilMatrix

Defines matrices.

ilMaxFltImg

Performs max filtering of an image

ilMaxImg

Computes the pixelwise maximum of two images

ilMedFltImg

Performs median filtering of an image

ilMemCacheImg

Implements data caching in main memory.

ilMemoryImg

Defines an image array resident in memory.

ilMergeImg

Merges several images into one.

ilMinFltImg

Performs minimum filtering of an image.

ilMinImg

Computes the pixelwise minimum of two images.

ilMonadicImg

Provides basic support for single-input operators.

ilMpLock

Provides an encapsulated version of ilSpinLock with a
different API that is more suitable for deriving from,
for example, to make a lockable list.

ilMpManager

Provides a generalized method to execute work in
parallel using a configurable number of threads
created with sproc.

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilMpPool

Manages a shared pool of resources, for example,
buffers. Groups of requests can acquire a particular
resource or get queued until the resource becomes
available.

ilMpQueue

Provides an abstract API for queueing ilMpRequest’s
to be executed by ilMpThread::run().

ilMpThread

Implements the abstraction of an execution thread
created with sproc.

ilMultiplyImg

Computes the pixelwise multiplication of two images.

ilNegImg

Performs two’s complement of an image.

ilNopImg

Provides caching on non-cached images.

ilOpImg

Provides basic support for operators.

ilOrImg

Computes a pixelwise logical OR of two images.

ilPage

Defines a page of image data in a cache.

ilPager

Implements a page table and manages an image cache.

ilPCDImg

Provides support for the Kodak Photo CD image pack
file format class.

ilPerspWarp

Manages a perspective warp.

ilPiecewiseImg

Performs a linear mapping of lookup table images.

ilPixelBufferFrag

Provides support to hardware managers for
allocating, locking, and drawing to pbuffer memory.

ilPixelBufferImg

Implements the ilImage model for images whose data
resides in off-screen frame buffer memory, that is, GL
p-buffers.

ilPixelCacheImg

Implements the ilImage model for images whose
data resides in off-screen frame buffer memory, that
is, GL p-buffers.

ilPolyDef

Defines some structures and methods for
polynomials.

331

Appendix D: Summary of All Classes

Table D-1 (continued)

332

Summary of All Classes

Class or Function

Description

ilPolyWarp

Specifies a two-dimensional seventh-order
polynomial warp.

ilPolyWarpImg

Performs a two-dimensional seventh-order warp.

ilPolyadicImg

Provides the base class for N-input operators.

ilPool

Implements a pooled memory allocation scheme with
facilities for compaction and reclamation of free space.

ilPowerImg

Raises image data to a specified power.

ilPriorityList

Lists items sorted by priority.

ilPropSet

Creates a collection of properties.

ilRankFltImg

Performs two-dimensional rank filtering on an image

ilRectRoi

Defines a rectangular region of interest (ROI).

ilRFFTfImg

Performs a real forward fast Fourier transform.

ilRFFTiImg

Performs a real inverse fast Fourier transform.

ilRGBImg

Converts to the RGB color model.

ilRobertsImg

Performs edge detection using Roberts kernels.

ilRoi

Defines an ROI.

ilRoiImg

Associates an ROI with an image.

ilRoiIter

Cycles through run lengths in an ROI.

ilRotZoomImg

Rotates, zooms, and flips an image.

ilSaturateImg

Performs color saturation of an image.

ilScale

Implements simple linear scaling operations, for
example: f(x) = ax + b.

ilScaleImg

Performs a linear scaling of an image.

ilSemaphore

Limits the number of process threads that can access a
shared data structure.

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilSepConvImg

Performs an image convolution using a separable
kernel.

ilSepKernel

Manages a separable kernel. A separable kernel is one
that can be separated into independent X and Y
components to optimize computation.

ilSGIPaletteImg

Converts to the RGB Palette color model.

ilSharpenImg

Sharpens an image.

ilSmallBitArray

Defines a bit array.

ilSobelImg

Performs edge detection using Sobel kernels.

ilSpace

Contains a list of IL to IFL compatibility #defines.

ilSpatialImg

Provides basic support for spatial operators.

ilSpinLock

Manages spinlock services.

ilSqRootImg

Computes the pixelwise square root of an image.

ilSquareImg

Computes the pixelwise square of an image.

ilStackAlloc

Provides a wrapper for alloca.

ilStackBuffer

Provides a four-dimensional, resizable buffer with
better performance than an ilBuffer.

ilStereoView

Associates a stereo view (two images) with a region in
an ilDisplayImg.

ilSubImg

Defines a rectangular portion of an image as an
independent image.

ilSubtractImg

Computes the pixelwise subtraction of two images.

ilSwitchImg

Implements a switch construct in an image operator
chain.

ilTOTAL

Implements the user interface portion of the interface
builder. Normally, it is not used directly. Instead the
subclass, FmtAttForm is instantiated.

ilTexImg

Implements a paged image stored in texture memory.

333

Appendix D: Summary of All Classes

Table D-1 (continued)

334

Summary of All Classes

Class or Function

Description

ilThread

Manages a shared group of processes.

ilThreshImg

Applies a threshold to an image.

ilTiePointList

Manages a list of tie points.

ilTieWarpImg

Warps an image by specifying tie points.

ilTIFFImg

Creates an image file in the TIFF format.

ilTileImgIter

Cycles through the pages spanning a tile.

ilTimeoutTimer

Provides a simple and efficient means of
implementing a timeout period for a polling loop.

ilTimer

Provides an interface to the high-resolution interval
timer. On most SGI machines, this timer has a
resolution of 1 usec or better.

ilVector

Provides a resizable Vector class.

ilVectorUtil

Provides vector utility routines.

ilView

Associates an image with a region in an ilDisplayImg.

ilViewCallback

Provides easy access to view state information as well
as other information needed for graphics callbacks.

ilViewer

Handles standard operations on ilDisplay objects
triggered by X events.

ilViewIter

Iterates through ilDisplay’s view stack.

ilWarp

Provides an abstract base class used to define 3D warp
functions.

ilWarp3Img

Derives from ilWarpImg and defines addrGen() to
evaluate a 3rd order polynomial.

ilWarpImg

Provides basic support for warping an image.

ilWarpRoamer

Provides an object that roams a warped image.

ilXDisplayImg

Defines an image that exists in the frame buffer.

ilXImage

Translates between an XImage and an ilImage.

Table D-1 (continued)

Summary of All Classes

Class or Function

Description

ilXWindowImg

Manages an IL interface to X Windows. IL maintains
internal ilDisplay and GL contexts to isolate rendering
from user code.

ilXorImg

Computes the pixelwise exclusive-OR of two images.

ilYCCconverter

Provides a YCC/RGB conversion object.

335

Appendix E

E. Implementing Your Own Image File Format

IFL supports a wide variety of image formats, including .tiff, .rgb, .rgba, .jpeg, and .gif. (For
a complete list of supported file formats, see “Supported IFL Image File Formats” on
page 66.) IFL is extensible, however, so that you can easily add support for additional file
formats. You do that by
1.

deriving your file format class from iflFile and iflFormat

2. implementing your derived class
3. adding your file format to the file format database, ifl_database
The file format supplied with IFL, FIT, provides the sample code described throughout
this chapter that demonstrates how you can extend IFL to implement your own file
format. The code for the FIT format is also available in the software distribution in
/usr/share/src/ifl/src/iflFITFile.c++

Although the C++ code might differ slightly from the excerpts shown in this chapter, the
functionality remains the same.
This chapter describes how to add and implement your own image file format.

Deriving and Implementing Your Image File Format Class
iflFile is an abstract, base class that you use to derive your image file format class. Every
iflFile object is an image file format class, such as iflTIFFFile (.tiff) and iflFITFile (.fit).
iflFile does not have a public constructor or destructor, so you cannot use iflFile directly.
In your new image file format class, you need to provide functions that
•

create a new file or open an existing one

•

read data from a file into the cache, one page at a time, decompressing it if necessary

•

write data from the cache into a file, one page at a time, compressing it if necessary

337

Appendix E: Implementing Your Own Image File Format

•

close a file

•

allow your format to be registered

To accomplish these tasks, your derived class will typically use the following inherited
member functions of iflFile that open, create, and close the file, flush the buffer, and parse
the file name:
•

open()

•

create()

•

close()

•

flush()

•

parseFileName()

These functions (and a class destructor) are the minimum number of functions your class
must provide. Very likely, your class will provide more capabilities including, perhaps,
your own version of reset() (declared in ilLink and ilImage) to handle altered parameters
properly.
The remainder of this section describes how the iflFile methods implement these
necessary tasks.

Opening an Existing File
You can specify a file by a filename, a file descriptor, or both. If both are specified, the file
descriptor is used to open the file. In this case, the filename is stored for use with error
messages and the iflFile::getfileName() method.
The iflFile::open() method is defined as follows:
iflFile* open(fileDesc, filename, mode, format, status);

where fileDesc is the file descriptor.
The name of the file, filename, can be followed by an index to specify sub-images using
the following syntax:
filename:index

338

Deriving and Implementing Your Image File Format Class

For more information about changing the index after a file is open, see “Functions that
Manipulate the Image Index” on page 348.
The mode argument specifies the read-write permissions set on the file. The two valid
mode are read-only, O_RDONLY, and read-write, O_RDWR.
The format argument specifies the file format for the opened file. If you set this argument
to NULL, the usual implementation, the file format is deduced from the file’s contents,
in particular, its magic number. You can, however, use the format argument to specify a
file format.
The status argument is set to an error value if the open() method fails. If you have not
implemented error messages, you should set this argument to NULL. If the method fails,
the return value of the method is NULL.
If the open() method succeeds, an iflFile* pointer is returned to a derived class of iflFile,
such as iflTIFFFile. The object created with open() can then be manipulated by the
methods in the derived class. It is the application’s responsibility to deallocate the object
using the iflFile::close() method, for example,
newFileObject->close();

The iflFITFile file format uses two constructors to open a file. The first constructor uses
just the filename to open the file, the second uses the file descriptor:
static iflFile* open(const char* filename, int mode = O_RDONLY,
iflStatus* status = NULL);
static iflFile* open(int fileDesc, const char* filename,
int mode = O_RDONLY, iflFormat* format = NULL,
iflStatus* status = NULL);

Example E-1 shows how ilFITFile.c++ implements opening a file.
Example E-1

Opening a File

iflStatus iflFITFile::openFile()
{
int fd_opened_here = 0;
iflStatus status;

339

Appendix E: Implementing Your Own Image File Format

if (fd < 0) {
assert(filename != NULL);
fd = (iflStatus)::open(filename, accessmode);
if (fd < 0)
return iflStatusEncode(iflOPENFAILED, iflSubDomainUNIX, ::oserror());
fd_opened_here = 1;
}
needHeader = 0;
dataWritten = 1;

// must be initialized for destructor!
// so extensions can’t be reserved

// read the header
if ((status = readHeader()) != iflOKAY) {
if (fd_opened_here) {
(void)::close(fd);
fd = -1;
}
return status;
}
// fill in other info
compression = iflNoCompression;
calcPageParams();
return iflOKAY;
}

Creating a New Image File
You can create a new file by specifying many of the same values you used in the open()
method. You can create a new file using a filename, a file descriptor, or both. If both are
specified, the file descriptor is used to create the file and the filename is stored for use
with error messages and the iflFile::getfileName() method.
The iflFile::create() method is defined as follows:
iflFile* create(fileDesc, filename, source, config, format, status);

All of the arguments in the create() method have the same mean as those described for
the open() method. Only the source and config arguments are different.

340

Deriving and Implementing Your Image File Format Class

The config argument is a structure defined in iflFileConfig that specifies a wide range of
file characteristics, including the file’s
•

dimensions

•

data type

•

dimension order

•

color model

•

orientation

•

compression

•

page dimensions

If any of these characteristics are not given, the source argument is used to define them.
If the source value is not defined, the characteristics default to the preferred values for the
file format.
The source argument points at an iflFile object. If any of the file’s characteristics are not
defined in the config structure, the characteristics are set to be the same as those of the
source object.
If the create() method succeeds, an iflFile pointer is returned to a derived class of iflFile,
such as iflFITFile. If the method fails, the method returns NULL and you can use the
status argument to set an error value.
Example E-2 shows how ilFITFile.c++ implements creating a file.
Example E-2

Creating a File

iflStatus iflFITFile::createFile()
{
// validate the c page size
if (pageSize.c == 0)
pageSize.c = order==iflSeparate? 1:size.c;
else if (order != iflSeparate && pageSize.c != size.c)
return iflStatusEncode(iflBADPARAMS);
if (fd < 0) {
assert(filename != NULL);
fd = ::open(filename, accessmode|O_CREAT|O_TRUNC, 0666);
if (fd < 0)
return iflStatusEncode(iflOPENFAILED, iflSubDomainUNIX, ::oserror());

341

Appendix E: Implementing Your Own Image File Format

} else {
(void)::ftruncate(fd, (off_t)0);
}
dataOffset = userOffset = sizeof(FIThead02);
// flag the header/data as not written
needHeader = 1;
dataWritten = 0;
calcPageParams();
scaleMinValue = iflDataMin(dtype);
scaleMaxValue = iflDataMax(dtype);
return iflOKAY;
}

Closing a File
Whether you open or create a new file object, you must write a destructor that terminates
it. This destructor needs to:
•

finish writing out any modified pages of image data to disk

•

close the file and release the file descriptor

•

free any temporary buffers that were allocated

You use the iflFile::close() member function to close files, defined as follows:
iflStatus close(int flags = 0);

where flags can be set to IFL_CLOSE_DISCARD which means that iflFile::flush() is not
automatically called so that buffered file data is not flushed when the file object is closed.
The close() method performs the following tasks:

342

•

flushes any buffered file data (unless the IFL_CLOSE_DISCARD flag is set)

•

closes the file

•

destroys the file object

Deriving and Implementing Your Image File Format Class

The close() method automatically calls the iflFile::flush() and iflFile::closeFile() methods
to carry out these tasks. Even if any of these three methods returns an error, the above
tasks are performed.
Note: The file descriptor is closed even if it is opened prior to the original iflFile::open()

or iflFile::create() call. To keep a file descriptor open, use dup() on the file descriptor
before closing the file and then pass the duplicated file descriptor to an open() or create()
method.
The following code shows how you might implement a destructor for a file format:
iflFileFormat::~iflFileFormat() {FileFormat->close();}

Example E-3 shows how ilFITFile.c++ implements closing a file.
Example E-3

Closing a File

iflStatus iflFITFile::closeFile()
{
assert(fd >= 0);
if (::close(fd) != 0)
return iflStatusEncode(iflCLOSEFAILED, iflSubDomainUNIX, ::oserror());
return iflOKAY;
}

Flushing the Buffer

The iflFile::flush() method is a virtual function that displays any buffered data associated
with an iflFile object. It is automatically called by iflFile::close() unless the environment
variable, IFL_CLOSE_DISCARD, is set. In this case, the data in the buffer is flushed but
not displayed.
You might like to call flush() before closing an image file if, for example, you want to
optimize memory space or system performance.
If flush() succeeds, it returns iflOKAY; if not, it returns an appropriate iflStatus error
value.
Example E-4 shows how ilFITFile.c++ implements flushing a buffer.

343

Appendix E: Implementing Your Own Image File Format

Example E-4

Flushing a Buffer

iflStatus
iflFITFile::flush()
{
// update the header if necessary
if (needHeader) {
iflStatus status = writeHeader();
if (status != iflOKAY) return status;
needHeader = 0;
}
return iflOKAY;
}

Parsing the File Name
iflFile::parseFileName() is a static class member function. It is used to parse a file name
for IFL. IFL file names have the following syntax:
[#][:]
[%]

This function is called automatically by the iflFile::open() member function. It is defined
as follows:
static char* parseFileName(const char* fullname,
char** formatName=NULL, int* index=NULL, char** formatArgs=NULL);

The return value is the actual filename and must be deleted by the user. The sub-image
index can be returned using index if it is non-NULL. If an index is not present in the
filename, -1 is returned. The format name can be returned using formatName if it is
non-NULL. If no format name is present in the filename, NULL is returned. The
format-specific argument string can be returned using formatArgs if it is non-NULL. If
format-specific arguments are not present in the filename. NULL is returned.

344

Deriving and Implementing Your Image File Format Class

Reading and Writing Formatted Data
In addition to providing functions that open and close files, you need to override
getPage(), setPage(), getTile(), and getPage() to read and write image data in your file
format. These functions are declared as follows:
virtual iflStatus getPage(void* data, int x, int y, int z, int c,
int nx, int ny, int nz, int nc);
virtual iflStatus setPage(const void* data, int x, int y, int z, int c,
int nx, int ny, int nz, int nc);
iflStatus getTile(int x, int y, int z, int nx, int ny, int nz,
void *data, const iflConfig* config=NULL);
iflStatus setTile(int x, int y, int z, int nx, int ny, int nz,
const void *data, const iflConfig* config=NULL);

The data argument for getPage() is a pointer to an already allocated, page-sized buffer
into which data is read. This buffer must be large enough to hold a page of data. For
setPage(), data is a pointer to a page-sized chunk of memory that contains data that will
be written to disk. These functions are described in greater detail in the following
sections.
These member functions are used by an IFL application to read image data from an
image file into memory, or to write image data from memory to an image file.
Standard practice is to use getTile(), setTile() functions. They enable reading and writing
of arbitrary rectangular regions, tiles, with an arbitrary datatype, dimension ordering,
and orientation.
Optimized applications may use the lower-level getPage(), setPage() functions. The
specified regions in these function are the file’s natural pages.
Using getTile()

This member function reads an arbitrary rectangular region from the image file into
memory. The portions of the memory buffer corresponding to the area outside of the
rectangular boundaries of the source file image are left undisturbed.
The arguments, x, y, z, nx, ny, nz, specify the origin and size of the desired tile within the
source image file, in the orientation indicated in the config parameter. The data argument
specifies the address of the memory buffer into which the data should be placed. The
config argument describes the configuration of the memory buffer. Its orientation affects
the interpretation of x, y, z, nx, ny, nz.

345

Appendix E: Implementing Your Own Image File Format

A successful call to getTile() returns iflOKAY. If an error occurs, an iflStatus error value
is returned. In an error condition, the buffer’s contents are undefined.
Using setTile()

This member function writes an arbitrary rectangular region from a memory buffer into
the image file. The portions of the memory buffer corresponding to area outside the
boundaries of the source file image are ignored.
The arguments x, y, z, nx, ny, nz specify the origin and size of the target tile within the
destination image file, in the orientation indicated in the config parameter. The data
argument specifies the address of the memory buffer containing the data to be written.
The config argument describes the configuration of the memory buffer. Its orientation
affects the interpretation of x, y, z, nx, ny, nz.
A successful call to setTile() returns iflOKAY. If an error occurs, an iflStatus error value
is returned describing the error. In an error condition, the buffer’s contents are
undefined.
Note: setTile() may make calls to the subclass’s getPage() and setPage() functions in

order to write a tile that is not aligned with the file’s pages.
Using getPage()

This virtual member function reads a page of image data from the image file. The data
argument specifies the address of the memory buffer into which the data should be
placed. The arguments x, y, z, nx, ny, nz specify the origin and size of the desired page
within the source image file. The caller must guarantee that nx, ny, nz are the image’s
page size and that x, y, z are a multiple of the page size. No checking is done by the
function.
A successful call to getPage() returns iflOKAY. If an error occurs, an iflStatus error value
is returned describing the error. In an error condition, the buffer’s contents are
undefined.
Note: If the caller makes multiple, concurrent calls to getPage() or setPage(), it needs to

set i/o callbacks.
getPage() is required to surround code that must be executed atomically by calls to
beginFileIO().

346

Deriving and Implementing Your Image File Format Class

Using setPage()

This virtual member function writes a page of image data from the image file.
The data argument specifies the address of the memory buffer containing the data to be
written. The arguments x, y, z, nx, ny, nz specify the origin and size of the desired page
within the source image file. The caller must guarantee that nx, ny, nz are the image’s
page size and that x, y, z are a multiple of the page size; no checking is done by the
function.
successful call to setPage() returns iflOKAY. If an error occurs, an iflStatus error value is
returned describing the error. In an error condition, the buffer’s contents are undefined.
Example E-5 shows how ilFITFile.c++ implements reading and writing data.
Example E-5

Reading and Writing Data in the FIT Format

iflStatus
iflFITFile::getPage(void* data, int x, int y, int z, int c, int,int,int,int)
{
iflStatus returnval = iflOKAY;
beginFileIO();
lseek (fd, pageOffset(x,y,z,c), SEEK_SET);
int sts = read(fd, data, pageSizeBytes);
if (sts == -1)
returnval = iflStatusEncode(iflREADFAILED, iflSubDomainUNIX,
::oserror());
endFileIO();
return returnval;
}
iflStatus
iflFITFile::setPage(const void* data, int x,int y,int z,int c,int,int,int,int)
{
iflStatus returnval = iflOKAY;
beginFileIO();
lseek (fd, pageOffset(x,y,z,c), SEEK_SET);
int sts = write(fd, data, pageSizeBytes);
if (sts == -1)
returnval = iflStatusEncode(iflWRITEFAILED, iflSubDomainUNIX,
::oserror());
endFileIO();

347

Appendix E: Implementing Your Own Image File Format

dataWritten = 1;
return returnval;
}

Functions that Manipulate the Image Index
An image file can contain more than one image, depending on the file format. For
example, the TIFF and GIF formats allow a file to contain any number of unrelated
images, and the Kodak Photo CD Image Pac (PCD) and JFIF formats allow access to
multiple resolutions of the same image. You can use image operations and queries on
each image in a file by using an iflFile object’s current image index.
The functions you can use to manipulate an image index are
•

getNumImg()

•

getCurrentImg()

•

setCurrentImg()

The application can change the index by calling the object’s setCurrentImg() method.
The current index and total number of images in the file can be queried by calling the
getCurrentImg() or getNumImg() method, respectively. The initial index may also be set
by specifying an index with the filename argument to iflFile::open().
Note: These operations are meaningful even if the file format does not support multiple

images per file. In that case, getNumImgs() returns 1, getCurrentImg() returns 0, and
setCurrentImg(i) will succeed only if i == 0.
The following sections describe these functions in greater detail.
Using getNumImgs()

This virtual member function returns the number of images contained in the image file.
The function is defined as follows:
virtual int getNumImgs();

348

Deriving and Implementing Your Image File Format Class

Using getCurrentImg()

This virtual member function returns the iflFile’s current image index. The first image in
the file is number zero. The second image in a file is number one, and so on.
The function is defined as follows:
virtual int getCurrentImg();

Using setCurrentImg()

This virtual member function sets the current image index to the specified value.
If the operation is successful, the function returns iflOKAY and the image index is
changed. If the argument given in the function is out of bounds of the images in the file,
the function returns iflStatusEncode(iflFILEFINDEXOOB) and the image index is left
unchanged.
If the operation fails for some other reason, an ifl error is returned using the iflError()
mechanism. If the program continues, the file’s image index will be in an unknown state.
The function is defined as follows:
virtual iflStatus setCurrentImg(int i);

Adding Images to Image Files
If an image file has write permissions and the file format supports the addition of images,
an application can use appendImg() to append an image to an image file. When the
function succeeds, the new image in the file is given an index number and the index
marker is updated to that of the added image.
The function is defined as follows:
virtual iflStatus appendImg(iflFile* source, iflFileConfig* fc=NULL);
The dimensions, datatype, dimensionorder, colormodel, compression, and pagedims parameters
specified in the iflFileConfig structure are treated the same as in the create() static
member function.

349

Appendix E: Implementing Your Own Image File Format

If the operation is successful, the function returns iflOKAY. If the operation fails, an ifl
error is returned using the iflError() mechanism. and the file’s contents and the object’s
current index will be in an unknown state.

Deriving an Image File Format from iflFormat
An iflFormat object describes the capabilities of an image file format. Usually, there is
only one such object per file format. Functions whose return values are of type iflFormat,
such as iflFile::getFormat(), generally return a pointer to the static iflFormat object of the
derived class.
iflFormat is an abstract base class. Every iflFormat object is actually a file-format-specific
subclass, such as iflTIFFFormat.

Deriving Subclasses
A derived class must define all of the following pure virtual functions found in iflFormat:
accessModeIsSupported()

randomAccessWriteIsSupported()

typeIsSupported()

getPreferredType()

orderIsSupported()

getPreferredOrder()

colorModelIsSupported()

getPreferredOrientation()

compressionIsSupported()

getPreferredColorModel()

sizeIsSupported()

getPreferredCompression()

pagingIsSupported()

getPreferredPageSize()

pageSizeIsSupported()

newfileobj()

randomAccessReadIsSupported()
Defining all of these functions fully characterizes the derived file format. newfileobj()
tells iflFile::open() how to create an iflFile object in the derived format.
Each image file format subclass must declare a single static object of its derived type.
Because the base class constructor is invoked automatically when the DSO is opened

350

Deriving an Image File Format from iflFormat

(because of the static object declaration) the derived format is placed on the global format
list as part of its initialization. The following code shows you a sample declaration:
static iflFITFormat theFITformat;

If you install ifl_dev.sw.gifts, you can check the source code provided in /usr/share/src/ifl
for examples of deriving from iflFormat.

Virtual Function Descriptions
This section describes all of the virtual functions listed in “Deriving Subclasses” on
page 350.
Table E-1

iflFormat’s Virtual Functions

Function

Description

accessModeIsSupported()

Tells whether the given access mode (which must be one
of O_RDONLY, O_WRONLY, or O_RDWR) is supported
by the subclass

typeIsSupported()

Tells whether the type is supported by the image file
format.

orderIsSupported()

Tells whether the order is supported by the image file
format.

colorModelIsSupported()

Tells whether the color model is supported by the image
file format.

compressionIsSupported()

Tells whether the compression is supported by the
image file format.

sizeIsSupported()

Tells whether the given image size x, y, z, c (which the
caller must guarantee are all positive values) is
supported by the image file format.

pagingIsSupported()

Tells whether the image file format supports any page
size other than the entire image size.

pageSizeIsSupported()

Tells whether the image file format supports the given
page size pWidth, pHeight, pz, pc for an image whose
size is iWidth, iHeight, iz, ic, which must be a supported
image size.

351

Appendix E: Implementing Your Own Image File Format

Table E-1 (continued)

iflFormat’s Virtual Functions

Function

Description

randomAccessWriteIsSupported()

Tells whether the iflFile subclass supports random
access reads or writes (as opposed to requiring that
reads or writes occur sequentially).

getPreferredType()

Returns the image file format’s preferred type attribute
value. The returned value must be one of the supported
values.

getPreferredOrder()

Returns the image file format’s preferred order attribute
value. The returned value must be one of the supported
values.

getPreferredOrientation()

Returns the image file format’s preferred orientation
attribute value. The returned value must be one of the
supported values.

getPreferredColorModel()

Returns the image file format’s preferred color model
attribute value. The returned value must be one of the
supported values.

getPreferredCompression()

Returns the image file format’s preferred compression
attribute value. The returned value must be one of the
supported values.

getPreferredPageSize()

Returns the image file format’s preferred page size
attribute value. The returned value must be one of the
supported values.

newfileobj()

Used by the static functions iflFile::open() and
iflFile::create() to construct an object of the derived class.
The implementation should only return a new object of
the desired subclass of iflFile.

randomAccessWriteIsSupported()

Tells whether the value of an image attribute is
supported by the image file format.

The following section shows you a code excerpt that defines all of these virtual functions.

352

Deriving an Image File Format from iflFormat

Sample Code for Virtual Function Definitions
Example E-6 shows an excerpt from iflFITFile.c++ that demonstrates how the virtual
functions in iflFormat are defined.
Example E-6

Defining Virtual Functions for Your Image File Format

class iflFITFormat : public iflFormat {
public:
iflFITFormat() : iflFormat(“FIT”) {}
int typeIsSupported(iflDataType) { return 1; }
int orderIsSupported(iflOrder) { return 1; }
int orientationIsSupported(iflOrientation) { return 1; }
int colorModelIsSupported(iflColorModel) { return 1; }
int compressionIsSupported(iflCompression cmp)
{ return cmp == iflNoCompression; }
int sizeIsSupported(int,int,int,int, iflOrientation)
{ return 1;}
int pagingIsSupported() { return 1; }
int pageSizeIsSupported(int,int,int,int, int,int,int,int,
iflOrientation)
{ return 1; }
int randomAccessReadIsSupported() { return 1; }
int randomAccessWriteIsSupported() { return 1; }
virtual iflDataType getPreferredType() { return iflUChar; }
virtual iflOrder getPreferredOrder() { return iflInterleaved; }
iflOrientation getPreferredOrientation()
{ return iflLowerLeftOrigin; }
virtual iflColorModel getPreferredColorModel(int nc)
{ return nc == 0? iflRGB : iflColorModelFromChans(nc); }
virtual iflCompression getPreferredCompression()
{ return iflNoCompression; }
void getPreferredPageSize(int, int, int, int sc,
int& pWidth, int& pHeight, int& pz, int& pc, iflOrientation)
{ pWidth = 128; pHeight = 128; pz = 1; pc = sc; }
iflFile* newfileobj() { return new iflFITFile; }
};
// This static object declaration causes the file format to be
// automatically registered when the DSO containing this code is
// dlopen’ed.
static iflFITFormat theFITformat;

353

Appendix E: Implementing Your Own Image File Format

Registering an Image File Format
To register your file format with IFL, you must create a dynamic shared object (DSO). A
DSO allows programs that use IFL to recognize your new file format without relinking
to those programs. The DSO contains the code for both your file format and the
registration object.
Each image file format subclass must declare a single static object of its derived type. The
base class constructor is invoked automatically when the DSO is opened (because of the
static object declaration). When the constructor is called, the derived format is placed on
the global format list as part of its initialization.
This following static object declaration for iflTIFFFormat causes the file format to be
automatically registered when the DSO containing this code is dlopen’ed.
static iflTIFFFormat theTIFFFormat;

Using the File Format Database
To add support for a new image file format, you create a Dynamic Shared Object (DSO)
that implements the format and adds an entry in the IFL file format database. IFL uses a
text database file to determine what file formats IFL can use. The database is normally
located in the file, usr/lib/ifl/ifl_database, but this location may be changed using the
IFL_DATABASE environment variable.

354

Registering an Image File Format

You can add your own file formats to the database manually by using the following
procedure:
1.

Create a file of image format descriptions using the following format:
format YourFormat
match
ushort(0) == 0x01da || ushort(0) == 0xda01
description “Your New Format”
dso
libiflYOURS.so
subsystem
“ifl_eoe.sw.c++”
suffixes
.yrfmt
format YourAlternateFormat
match ulong(0) == 0x49492a00 || ulong(0) == 0x4d4d002a
description
“Your Alternate Format”
dso
libiflALT.so
subsystem
“ifl_eoe.sw.c++”
suffixes
.altfmt

2. In the file you created in step one, use the #include directive to include the file
ifl/src/ifl_database, which defines all ifl-supported image file formats.
3. Set the IFL_DATABASE environment variable to point at the file created in step one.

355

Appendix F

F. Auxiliary Classes, Functions, and Definitions

This appendix describes IL classes not fully discussed elsewhere in this guide. It also lists
all the error codes and enumerated types used by the IL. This appendix has the following
major sections:
•

“Auxiliary Classes” on page 358 briefly discusses the iflBitArray, ilBuffer and
ilStackAlloc, iflConfig, ilKernel, iflLut, ilMatrix, ilPage, iflPixel, iflSize, and iflTile
classes.

•

“Useful Functions” on page 362 describes several functions that does not belong to
any particular class. They are useful for such tasks as computing the size of IL data
types and for performing minimum and maximum comparisons.

•

“Convenient Structures” on page 365 lists the definitions of the ilCoord, iflSize, and
various coefficient data structures.

•

“Error Codes” on page 366 lists the error codes used by the IL.

•

“Enumerated Types and Constants” on page 369 gives an annotated list of the
enumerated types and constants defined in the IL.

357

Appendix F: Auxiliary Classes, Functions, and Definitions

Auxiliary Classes
All of the classes described in this section have their own reference pages; refer to them
for more specific information about using these classes.

358

•

The iflBitArray class implements a subscriptable bit array of limited functionality
for conveniently operating on bit data.

•

ilBuffer (allocated from the heap) and ilStackAlloc (allocated from the stack) are
standalone objects that provide support for accessing a buffer in up to four
dimensions. The call operator, (), is overloaded to operate on either type of buffer
and returns a pointer to the specified element in the buffer. In addition, the an
ilBuffer can be resized after being created. An ilStackAlloc is recommended for use
in derived operators, since it tends to fragment the memory less than an ilBuffer
does, thus resulting in better performance for an application. However, an
ilStackAlloc cannot be resized.

•

The iflConfig class is used in ilImage functions such as getTile() and setTile() to
describe the configuration of pixel data. You can also use it when constructing an
ilSubImage to map the configuration of the input image to that of the subimage.
This class is described in more detail in “iflConfig” on page 359.

•

ilKernel is the base class for a three-dimensional kernel. The kernel elements are
stored in row-major form. An ilKernel is defined by x, y, and z dimensions, the
kernel data, the kernel origin, and the data type of its elements. ilKernel also
provides functions to access kernel attributes and data. ilSepKernel is derived from
ilKernel for representing separable kernels. ilSepKernel enables access to x, y, and z
kernels separately.

•

The iflLut class is used to access and manipulate lookup tables. This class is
described in more detail in “Using iflLut” on page 360.

•

The ilPage class is used to describe rectangular regions of an image in a cache (that
is, pages). This class groups the eight values describing the origin (x,y,z,c) and size
(nx,ny,nz,nc) of a page together in a convenient way.

•

The iflPixel class abstracts the concept of a pixel of image data. It contains the data
type, the number of channels, and a list of component values. Pixels are used as
arguments to a number of ilImage functions and to some operator image
constructors and functions.

•

iflSize is used to describe the size of an IL image. This class groups the four values
describing the size (x,y,z,c) of an image together in a convenient way.

Auxiliary Classes

•

The iflTile class is used to describe arbitrary rectangular regions of an image (that is,
tiles). This class groups the six values describing the origin (x,y,z) and size (nx,ny,nz)
of a rectangle together in a convenient way.

iflConfig
The header file ifl/iflConfig.h defines the class iflConfig, which is a structure used to
describe the configuration of pixel data on getTile() and setTile() calls. The fields of
iflConfig describe the data type, pixel ordering, number of data channels, ordering of
data channels, channel offset, coordinate space, and color model (the color model field is
currently ignored by functions such as getTile() and setTile()). The code in Example F-1
shows the iflConfig constructors and fields.
Example F-1

iflConfig Constructors and Fields

iflDataType dtype;
iflOrder order;
iflOrientation orientation;
int nchans;
int choff;
int* channels;
iflConfig() {}
iflConfig(iflDataType type, iflOrder ord=iflInterleaved, int nchan=0,
int* chanList=NULL,int chanOff=0,
iflOrientation ori=iflOrientation(0));
~iflConfig() {}
void invert(int nc, int* chanList) const;
int isInvertable() const;
void compose(int nc, int* in, int* out) const;
int mapChan(int idx) const
{ return (idx<0||idx>=nchans)? -1:
(channels!=NULL? channels[idx+choff]:idx+choff); }
int operator[](int idx) const
{ return mapChan(idx); }
};

The fields of an iflConfig are set with its constructor. The data type argument is required;
the other arguments are optional. The channel list defines what channels of a source

359

Appendix F: Auxiliary Classes, Functions, and Definitions

image are mapped into a destination image; the channel offset defines where to start
counting the source channels as zero. For example, consider a source image with 11
channels (0...10) and suppose you wish to map channels 4, 5, and 6 to a destination
image. You can do this by setting the number of channels to 3 and the channel offset to 4
(so that the first channel mapped is 4, and the next 3 channels in the source define all 3
channels). No channel list is necessary. Alternatively, you can set the number of channels
to 3, the channel offset to 0, and the channel list to 3, 4, 5. The hints field is reserved for
internal IL use only.
invert() is used to create a channel list of nc channels (written into chanList) that describes
an inverse mapping between two images. For example, a source image defines three
channels (0, 1, 2) and you have mapped 0 to 2, 1 to 0, and 2 to 1 in a destination image
(the channel list to do this is 1, 2, 0). To map the destination to the source instead, use
invert(). (This is useful to avoid creating a temporary buffer when copying from an
ilDisplayImg to an ilOpImg, for example.) In the above example, the resulting channel
list is 2, 0, 1. isInvertable() is used to determine whether the channel mapping has an
inverse.
compose() is used to compose a channel list from a subset of another; you supply the
number of channels, nc, and the subchannel list, in, and compose() writes its result to out.
For example, a source image defines three channels (0, 1, 2) and you have mapped 0 to 2,
1 to 0, and 2 to 1 in a destination image (the channel list to do this is (1, 2, 0). However,
the source image is actually an ilSubImg (a subimage of another ilImage) that contains
no data itself. It specifies a subset of its parent image’s channels; they are 2, 4, and 6 (so
it uses an iflConfig with channel list 2, 4, 6). To map directly from the source’s parent
image to the destination image, you need a composed channel list. The ilSubImg’s
channel list is specified as in, and the 3 values mapped to out are 4, 6, 2.
The member function mapChan() returns the contents of channels (the channel list) at the
specified index added to the channel offset specified by choff. The index operator, [], is
overloaded to perform the same function as mapChan(); both indicate what channel in
the source maps to the specified channel in the destination. Both return -1 if the supplied
index is less than 0 or greater than the number of channels.

Using iflLut
The header file ifl/iflLut.h defines a class, iflLut, used to describe lookup tables. The
elements used to define an iflLut are the number of channels, the data type, the table
length, and the table data. The code in Example F-2 shows the constructors and member
functions for iflLut.

360

Auxiliary Classes

Example F-2

iflLut Constructors and Member Functions

iflLut()
{ init(NULL, 0, iflDataType(0), 0, 0); }
iflLut(int numChan, iflDataType dtype, double min, double max, int length=0)
{ init(NULL, numChan, dtype, min, max, length); }
iflLut(void* table, int numChan, iflDataType dtype,
double min, double max, int length=0)
{ init(table, numChan, dtype, min, max, length); }
virtual ~iflLut();
int getNumChans() const { return numChannels; }
iflDataType getDataType() const { return type; }
int getLength() const { return tabLength; }
double getVal(double domainIdx, int chan=0) const;
iflStatus setVal(double val, double domainIdx, int chan=0);
void* getOrigin(int chan) const;
void* getChan(int chan) const;
void* getData() const { return data; }
void setData(void* dataPnt);
void getDomain(double& min, double& max) const
{ min = domainMin; max = domainMax; }
double getDomainMin() const
{ return domainMin; }
double getDomainMax() const
{ return domainMax; }
double getDomainStep() const
{ return 1/scale; }
void getRange(double& min, double& max) const;
iflStatus setDomain(double min, double max);
int isDiff(const iflLut& from) const;

The first constructor takes a NULL argument and is only useful with assignment
operators.
The second constructor allocates a lookup table (LUT) and takes control of the data. The
minimum and maximum values specify the domain of values that the LUT maps. You
can use the length argument to constrain the resolution of the LUT. The default value for

361

Appendix F: Auxiliary Classes, Functions, and Definitions

length, if you do not specify it, is the maximum value minus the minimum value plus
one (max. - min. + 1). This formula creates a one-to-one mapping of LUT index to LUT
entry.
The third constructor wraps an iflLut object around user-specified data. The object does
not copy the user data, however, it just retains a pointer to it.
The getNumChans(), getDataType(), and getLength() functions return basic attributes
of the lookup table. The length reflects the actual number of entries in the table, not
necessarily the domain of accepted values.
The getVal() and setVal() functions are the primary methods to access table entries. The
domanIdx is scaled appropriately based on the minimum or maximum range and length
of the table to access the corresponding table entry
The getOrigin() function returns a pointer to the first (0) entry in the LUT (even it if is off
the physical table). getChan() returns a pointer to the beginning of the physical table for
a specified channel. getData() returns a pointer to the beginning of the tables for all of the
channels. The internal layout of the tables is channel sequential, not interleaved.
setData() enables you to set table values.
The domain functions return and set information on the domain and range of the lookup
table. You specify the minimum and maximum values of the domain in the constructor
or by calling setDomain(). You can return the minimum and maximum values by calling
getDomainMin() or getDomainMax(), respectively.
A table domain is defined by the minimum and maximum values specified in an iflLut
constructor or in a setDomain() function. The domain step, returned by
getDomainStep(), is the stepping factor used to read physical table values sequentially.
The range value, returned by getRange(), is calculated by finding the difference between
the maximum and minimum table entry values.
The isDiff() function compares two lookup tables an returns TRUE if there are any
differences.

Useful Functions
This section describes utility functions defined by the IFL. These functions do not belong
to any particular class, so they can be used anywhere in an IL program.

362

Useful Functions

Computing the Size of Data Types
The IFL defines constants that correspond to the data types it uses; it also defines a
related set of functions for determining the sizes and possible values for these types.
These constants are defined as the iflDataType enumerated type in the header file
ifl/iflTypes.h:
enum iflDataType {
iflBit
= 1,
iflUChar
= 2,
iflChar
= 4,
iflUShort
= 8,
iflShort
= 16,
iflULong
= 32,
iflLong
= 64,
iflFloat
= 128,
iflDouble
= 256
};

/*
/*
/*
/*
/*
/*
/*
/*
/*

single-bit */
unsigned character (byte) */
signed character (byte) */
unsigned short integer (nominally 16 bits)*/
signed short integer */
unsigned long integer */
long integer */
floating point */
double precision floating point */

The following two functions perform computations using the above data types. They are
defined in the header file ifl/iflDataSize.h and described in the iflDataSize reference page.
size_t iflDataSize(iflDataType type, int count = 1);
iflDataType iflDataTypeFromRange(double minVal, double maxVal,
int typeMask=-1);
iflDataType iflDataClosestType(iflDataType desired, int allowed,
int flags=0);
double iflDataMin(iflDataType);
double iflDataMax(iflDataType);
int iflDataIsSigned(iflDataType);
int iflDataIsIntegral(iflDataType);

The first function, iflDataSize(), returns the number of bytes needed to store count
elements of data type. By default, count is 1. Conversely, iflDataTypeFrom Range()
returns the first IL data type that is large enough to hold the range of values specified by
minVal and maxVal.
iflDataClosestType() returns an allowable data type that most closely resembles the
input data type.
iflDataMax() and iflDataMin() return the maximum and the minimum possible values,
respectively, for the specified data type.

363

Appendix F: Auxiliary Classes, Functions, and Definitions

If you pass one of the iflDataTypes as an argument for iflDataIsSigned(), this function
returns TRUE if the type is signed and FALSE (zero) otherwise. Remember that ilImage
defines a similar function for an image, isSigned(), that returns TRUE if the image’s data
type is signed.
If you pass one of the iflDataTypes as an argument for iflDataIsIntegral(), this function
returns TRUE if the type is integral or FALSE (zero) otherwise.

Minimum and Maximum Comparisons
The header file ifl/iflMinMax.h defines several in-line functions that determine the
minimum and the maximum of two to four input values, as shown below:
template inline T iflMin(T a, T b);
template inline T iflMax(T a, T b);
template inline T iflMin(T a, T b, T c);
template inline T iflMax(T a, T b, T c);
template inline T iflMin(T a, T b, T c, T d);
template inline T iflMax(T a, T b, T c, T d);

The iflMin() function returns the lesser of the input values, and iflMax() returns the
greater of the input values.

Converting to Color-index Mode
iflColorModelFromChans(), in ifl/iflColor.h., converts a channel to the closest
corresponding value in the standard color map that is used in color-index mode.
iflColorModel iflColorModelFromChans(int nc);
int iflColorModelHasAlpha(iflColorModel cm);
int iflColorModelChans(iflColorModel cm);

iflColorModelChans() determines the number of channels for a given color model cm.
iflColorModelHasAlpha() determines whether or not alpha information is present in the
image data.

364

Convenient Structures

Convenient Structures
This section lists the definitions of the iflCoord and various coefficient data structures.

Coordinate Data Structures
The structures listed in Table F-1 hold two- (x,y), three- (x,y,z), and four-dimensional
(x,y,z,c) coordinates of various data types; they are defined in the ifl/iflCoord.h header file.
iflXYC**, iflXYZC**, and iflXYZC** are simple structures without any constructors,
destructors, or convenience operators.
Table F-1

Coordinate Data Structures

Two-dimensional

Three-dimensional

Four-dimensional

iflXYchar, iflXYCchar

iflXYZchar, iflXYZCchar

iflXYZCchar, iflXYZCchar

iflXYint, iflXYCint

iflXYZint, iflXYZCint

iflXYZCint, iflXYZCint

iflXYfloat, iflXYCfloat

iflXYZfloat, iflXYZCfloat

iflXYZCfloat,
iflXYZCfloat

iflXYdouble,
iflXYCdouble

iflXYZdouble,
iflXYZCdouble

iflXYZCdouble,
iflXYZCSdouble

These structures are defined in ifl/iflCoord.h as follows:
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct

iflXYchar
iflXYint
iflXYfloat
iflXYdouble
iflXYZchar
iflXYZint
iflXYZfloat
iflXYZdouble
iflXYZCchar
iflXYZCint
iflXYZCfloat
iflXYZCdouble

{
{
{
{
{
{
{
{
{
{
{
{

char
int
float
double
char
int
float
double
char
int
float
double

x,
x,
x,
x,
x,
x,
x,
x,
x,
x,
x,
x,

y;
y;
y;
y;
y,
y,
y,
y,
y,
y,
y,
y,

};
};
};
};
z;
z;
z;
z;
z,
z,
z,
z,

};
};
};
};
c;
c;
c;
c;

};
};
};
};

365

Appendix F: Auxiliary Classes, Functions, and Definitions

Error Codes
Error codes are contained in il/ilStatus.h and ifl/iflStatus.h. The function getStatus()
returns an ilImage’s current status. Many other functions return the type ilStatus or
iflStatus.
This section describes all of the error codes.

ilStatus Error Codes
Table F-2 describes the error messages found in ilStatus.h.
Table F-2

366

ilStatus Error Codes

Erro Message

Description

ilOKAY

Successful operation

ilBADFILEREAD

Error reading from file

ilBADFILEWRITE

Error writing to file

ilBADMALLOC

malloc() or new returned NULL

ilBADIMGFMT

Bad image file format

ilBADDIMS

Bad dimensions

ilBADOBJ

Bad object on construction

ilBADATTR

Bad attributes

ilFMTUNSUP

Unsupported file format

ilBADPIXTYPE

Bad pixel type

ilBADCONFIG

Unsupported configuration

ilNORANDOMSEEK

Cannot do random seek

ilBADSEEK

Error seeking on file

ilBADDECODE

Failure on decompression

ilREADONLY

Object is not writable

Error Codes

Table F-2 (continued)

ilStatus Error Codes

Erro Message

Description

ilBADFIELDSET

Failed to set field in file header

ilBADCOMPRESSION

Invalid image compression

ilNULLOBJ

NULL object passed as parameter

ilBADINPUT

Invalid input passed

ilBADCOLFMT

Bad color format

ilBADOP

Bad operation attempted

ilBADFILEOPEN

Error opening file

ilBADMAGIC

Invalid magic number in file

ilEMPTYFILE

File is empty

ilDATACLIPPED

Data has been clipped

ilOUTOFBOUND

Parameter(s) out of bounds

ilTOOMANYLOCKED

Too many pages locked in image cache

ilLUTSIZEMISMATCH

Incompatible number of channels in lut and image

ilZERODIVIDE

Attempted to divide by zero

ilUNSUPPORTED

Attempted operation is unsupported

ilUSEDOLDLIMITS

Used old limits for histogram calculation

ilBADPAGEDIMS

TIFF page dimensions must be multiples of 8

ilBADTIFFDIR

Could not index into TIFF directory

ilNOTRESIDENT

Page is not resident in cache

ilHWACCELFAIL

Unable to complete hardware accelerated operation

ilHWACCELNEVER

Unable to complete hardware acceleration operation

ilPARKED

Request has been parked

ilUNIMPLEMENTED

Unimplemented

ilIFL_ERROR

IFL error

367

Appendix F: Auxiliary Classes, Functions, and Definitions

Table F-2 (continued)

ilStatus Error Codes

Erro Message

Description

ilFAILED

Operation failed

ilNOTLOCKED

Unlocked ilLockRequest

ilBADINPUTSTATUS

Input image has bad status

ilABORTED

Operation was aborted

ilAPPROXIMATE

Result is not exact

iflStatus Error Codes
Table F-3 describes the error messages found in iflStatus.h.
Table F-3

368

iflStatus Error Codes

Error Messages

Description

iflOKAY

Successful operation

iflREADONLY

Image file is read-only

iflWRITEONLY

Image file is write-only

iflBADPARAMS

Bad parameters

iflUNSUPPORTEDBYLIBRARY

Non-IFL library call.

iflUNSUPPORTEDBYFORMAT

Unsupported image format

iflBADMAGIC

Bad magic number, unrecognizable file type

iflBADIMGFMT

Bad image

iflBADFIELDSET

Failed to set field in file header

iflBADFIELDGET

Failed to get field in file header

iflSYSTEM_CONFIGURATION_ERROR

Configuration error

iflFILEINDEXOOB

File index out of bounds

iflMALLOCFAILED

Malloc failed

Enumerated Types and Constants

Table F-3 (continued)

iflStatus Error Codes

Error Messages

Description

iflOPENFAILED

Error in opening file

iflCLOSEFAILED

Error in closing file

iflREADFAILED

Error in reading file

iflWRITEFAILED

Error in writing file

iflSEEKFAILED

Error seeking on file

iflSTATFAILED

Error in state

iflDBOPENFAILED

Failed when opening file format database,
ifl_database

iflSCRIPTFAILED

Script failed

Enumerated Types and Constants
The IL uses enumerated types and defined constants extensively; they are defined in
header files such as ifl/iflDefs.h and il/ilDisplayDefs.h. This section lists these types and
constants in the following functional groups, according to what they are used for:
describing image attributes, controlling the effect of operators, and controlling the
display facility. All of these types are described in more detail in the relevant chapters of
this guide.
Also note that NULL, TRUE, and FALSE have been defined as follows in the header file
ifl/iflDefs.h:
#ifndef NULL
#define NULL 0
#endif
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0

369

Appendix F: Auxiliary Classes, Functions, and Definitions

Describing Image Attributes
ilDisplayDefs.h contains the remaining definitions used when describing image
attributes.
/*
* Display mode bit fields (subject to change)
*
* 0xDDCCBBAA where:
*
* AA = Wipe/Align/Split Modes
* BB = Param/Del Modes
* CC = Defer/Clip/Stop Modes
* DD = Paint/Display Modes
*
*/
/*
* ilWipeMode passed as mode for display and wipe, and returned from
* findView and findViewEdge
*/
enum ilWipeMode {
ilNoView
= 0x00, /* No view found by findView()
*/
ilTopEdge
= 0x01, /* wipe top edge, display image from top edge
*/
ilBottomEdge
= 0x02, /* wipe bottom edge, display from bottom edge
*/
ilLeftEdge
= 0x04, /* wipe left edge, display image from left edge */
ilRightEdge
= 0x08, /* wipe right edge, display image from right edge*/
ilAllEdge
= 0x0f, /* wipe operated as inset, display at center
*/
ilNoEdge
= 0x10, /* No edge found by findViewEdge()
*/
ilWipeMask
= 0x1F
};
/*
* ilAlignMode specifies the display() operator specific modes.
* Combinations of ilWipeMode can alternatively be used for the first 5
*/
enum ilAlignMode {
ilTopLeft
= 0x05, /* align view/image to top left corner
ilBottomLeft
= 0x06, /* align view/image to bottom left corner
ilTopRight
= 0x09, /* align view/image to top right corner
ilBottomRight
= 0x0a, /* align view/image to bottom right corner
ilCenter
= 0x0f, /* align view/image to center of image
ilNoAlign
= 0x10, /* do not re-align (unchanged)
ilAlignMask
= 0x1F
};

370

values

*/
*/
*/
*/
*/
*/

Enumerated Types and Constants

/*
* ilParamMode specifies how to interpret passed parameters
*/
enum ilParamMode
{
ilDelVal
= 0x00000100,
/* Delta relative to current
ilAbsVal
= 0x00000200,
/* Absolute value
ilRelVal
= 0x00000400,
/* Relative to start xy
ilOldRel
= 0x00000800, /* ilRelVal but start xy not updated
ilParamMask
= 0x00000f00
};
/*
* Specifies various display modes
*
*/
enum ilDispMode
{
ilDefault
= 0x00000000,
ilClip
= 0x00001000,
ilDefer
= 0x00002000,
ilNoSwap
= 0x00004000,
ilDop
= 0x00008000,
ilDispMask
= 0x0000f000,
ilDspCoord
= 0x00010000,
ilScrCoord
= 0x00020000,
ilCoordMask
= 0x00030000,
ilDefaultCmap
= 0x00040000,
ilDestroy
= 0x00080000
};

*/
*/
*/
*/

such as whether to clip or defer painting

/*
/*
/*
/*
/*

no clip, no defer, swap
clip to display/image
defer painting
don’t swap buffers
override Nop flag

*/
*/
*/
*/
*/

/*
/*
/*
/*
/*

ilDisplayImg coordinates passed
screen coordinates passed
for internal use only
use default colormap
internal use only

*/
*/
*/
*/
*/

/*
* ilSplitMode specifies how to split the views in ilDisplay.
*/
enum ilSplitMode
{
ilRelSplit
= 0x00010000,
/* Split & pos image relative to view pos*/
ilAbsSplit
= 0x00020000,
/* Split & pos image at origin
*/
ilRowSplit
= 0x00040000,
/* Split into rows
*/
ilColSplit
= 0x00080000,
/* Split into columns
*/
ilPackSplit
= 0x00100000,
/* Split views and pack together (if
clipped) */
ilSplitMask
= 0x001f0000
};

371

Appendix F: Auxiliary Classes, Functions, and Definitions

/*
* ilLocMode is used by getLoc() and setLoc() to find xy location
* of a pixel in image and move image or view to specified location.
*/
enum ilLocMode {
ilLocIn
= 0x00100000, /* locate xy in image’s input space
*/
ilLocOut
= 0x00200000, /* locate xy in image’s output space */
ilLocImg
= 0x00400000, /* locate by moving image
*/
ilLocView
= 0x00800000, /* locate by moving view
*/
ilLocMask
= 0x00f00000
};
/*
* Image Render Modes
*/
enum ilRender {
ilGLRender
= 1,
ilXRender
= 2
};

/* Render image using GL */
/* Render image using X */

#ifndef __cplusplus
typedef enum ilRender ilRender;
#endif
/*
* Miscellaneous
*
*/
enum ilDispMisc {
ilLast
ilDefaultMargin
ilHighlight
};

= -1,
/* add view to bottom of viewStack */
= 15,
/* default margin width for findEdge etc */
= 0x10 /* find view and highlight its borders
*/

/*
* ilViewer modes
*/
enum ilAreaOption {
ilBadArea
= 0,
ilViewedImage
= 1,
ilFullImage
= 2,
ilFullWindow
= 3
};

372

/*
/*
/*
/*

invalid */
the area of the image the selected view covers */
the entire image associated with selected view */
the entire window, all views */

Enumerated Types and Constants

#ifndef __cplusplus
typedef enum ilAreaOption ilAreaOption;
#endif
;}

373

Appendix G

G. Using the Electronic Light Table

The Electronic Light Table (ELT) operator implements a chain of operators in hardware.
This implementation allows you to view and manipulate image files, compressed or not,
in real time. ELT also allows you to add graphics and text on top of manipulated images.
Viewing the processed images in real time avoids the necessity of first storing them first
on disk before viewing it. Most often you use an ELT application to look at huge images
(10K X 10K or larger).
This chapter describes the ilELTImg operator and how you use it along with ilDisplay,
ilView, and ilStereoView to create an ELT application. This chapter contains the following
major sections:
•

“Understanding How ELT Works”

•

“Setting Operator Values”

•

“Understanding Accelerated Performance”

•

“Choosing a Display in ELT Applications”

•

“Creating an ELT Application”

•

“Understanding the ilELTImg API”

Understanding How ELT Works
You can think of the ELT as an image-processing pipeline, as shown in Figure G-1.

375

Appendix G: Using the Electronic Light Table

ilChain
R Sets

R0
R1
R2
R3
R4
R5
R6
R7
.
.
.

Display
DeWarp

RotZoom

ELT controls input
so the correct R-set
is used for a specific
rotzoom value

Convolve

LUT

Histogram

ELT

Figure G-1

ELT image processing pipeline

Each stage of the ELT pipeline has the following purpose:

376

R Sets

contain the image data at different levels of reduction.

DeWarp

changes the perspective or corrects any image imperfection caused by
the image capturing mechanism.

RotZoom

allows you to zoom or rotate the image. ELT selects the correct R-set to
input for each specified zoom value.

Convolve

allows you to sharpen or blur the image.

Histogram

feeds data into the LUT to adjust pixel luminance dynamically
according to the overall brightness of the displayed image.

LUT

adjusts pixel luminance and color dynamically.

Understanding How ELT Works

DeWarping the Image
The output pages are subdivided into fine meshes of a user-defined size. The default size
is a uniform, 10 X 10 mesh. The dewarp function composited with an affine
transformation is used to compute the coordinates of the input image. Using a fine mesh
with a high order resampling method, either bilinear or bicubic interpolation, creates
high quality dewarped images.
The member functions used to get and set both dewarp and mesh parameters are
ilStatus setWarp(ilELTrset* rset, const ilWarp* Xyc);
ilStatus getWarp(ilELTrset* rset, const ilWarp*& Xyc) const;
void setMaxMeshSize(int n);
int getMaxMeshSize() const;
ilStatus setResampType(ilResampType rs);

rset allows the application to reset the warp operator, Xyc is the dewarp function, n is the
maximum mesh size, and rs is either ilNearNb, ilBiLinear or ilBiCubic.

RotZooming the Image
The RotZooming operator enables fluid zoom, rotation, and translation of images. You
use the following ilELTImg member functions to control the view manipulation:
void setAngle(float ang);
float getAngle() const;
void setZoom(float s);
float getZoom() const;
void setHorizontalFlip(int flip);
int getHorizontalFlip();
void setVerticalFlip(int flip);
int getVerticalFlip(); }

where ang is the angle to display the image at, myELT is the ilELTImg object, s is the zoom
level, and flip indicates that the image should be flipped.

Convolving the Image
The ilELTImg class also provides member functions for convolving an image using either
general or separable kernels.

377

Appendix G: Using the Electronic Light Table

void setConvKernel(ilKernel* inputKernel);
const ilKernel* getConvKernel() const;

where inputKernel is the kind of convolution kernel you want to set. The kernel may be
either an ilKernel- or ilSepKernel-type of object.
Kernels with sizes of 3 X 3, 5 X 5, or 7 X 7 can be accelerated on the Impact, RealityEngine,
and InfiniteReality platforms. Separable convolutions run faster than general
convolutions.

Collecting Histogram Data
Histogram data is used by the look-up table operator, iflLutImg, to automatically adjust
the color and luminance of each pixel to spread the range of luminance in an image over
the number of bits per pixel in the output image. Transposing the luminance values in
this way has the effect of making dark images brighter and overly-bright images darker.
Histogram data is collected over a user-definable number of frames. If, for example, the
sampling interval is defined as thirty frames, data is collected over the first 28 frames,
during the display of the 29th frame the data is read out of the hardware, and during the
30th frame the information is sent to the LUT. In general, data is collected over the
sampling interval minus two frames (samplingInterval - 2).
Collecting and processing histogram data incurs a performance drawback. To alleviate
this problem, you can specify the histogram sampling interval. Performance improves if
the histogram data is sampled infrequently. The possible consequence of a long sampling
interval, however, is sudden adjustments to luminance and color. The default update
interval is once per 30 frames.
Use the following ilELTImg member functions to control the sampling rate:
ilStatus setHistPeriod(int numFrames);
int getHistPeriod() const;

where numFrames is the number of frames that pass between one sampling and another.
Use the following functions to specify the number of bins used to compute the histogram
tables:
ilStatus setHistNbins(int num);
int getHistNbins() const;

378

Understanding How ELT Works

where num is the number of bins.
All channels have the same number of bins and the number of bins must be a power of
two.
Use the following functions to process the histogram tables that are returned:
ilStatus setHistCallback(const ilCallback* callback = NULL);
ilCallback* getHistCallback() const;

Dynamically Adjusting the Image
The look-up table (LUT) adjusts the luminance and color values of each pixel
dynamically. The LUT can adjust the luminance and color values on a frame-by-frame
basis. The actual adjustment is determined by how often the histogram samples the
image. The default is once every 30 frames.
The process of Dynamic Range Adjustment (DRA) takes the darkest and brightest
luminance values in the image and scales them over the number of bits per pixel in the
output image. This has the effect of making dark images bright and overly-bright images
darker.
The Tonal Transfer Characteristic (TTC) changes the image colors. These changes can
either correct color errors introduced by a camera mechanism or create false colors for
the purpose of examining image details.
The member functions used to get and set DRA and TTC are
ilStatus setLookUpTable(const iflLut& lut);
const iflLut* getLookUpTable() const;

where lut contains information from the lookup table.

DeWarping the Image Data
Before displaying the image manipulated by the ELT chain, you can add vectors, shaded
surfaces, or text on top of the images by using the callback functions provided by
ilELTImg and ilDisplay. Four callbacks are provided in the ilELTImg class for
•

adjusting the contrast

379

Appendix G: Using the Electronic Light Table

•

computing the bounding box

•

generating triangle meshes

•

deleting buffer resources

The callbacks that adjust the contrast are discussed in “Collecting Histogram Data” on
page 378. The other callback functions include
void setBBoxCallback(const ilCallback* callback = NULL);
ilCallback* getBBoxCallback() const;
void setTmeshCallback(const ilCallback* callback = NULL);
ilCallback* getTmeshCallback() const;
void setTmeshDelCallback(const ilCallback* callback = NULL);
ilCallback* getTmeshDelCallback() const;

The bounding box callback returns the vertices of the input space. If NULL is passed into
the setBBoxCallback() function, the default bounding box calculations method is used.
The mesh callbacks generate the triangle mesh necessary to dewarp an image. If NULL
is passed into the setTmeshCallback() function, the default Tmesh calculations method
is used.
You can either delete allocated memory for the Tmesh explicitly using
setTmeshDelCallback(), or let it deallocate itself automatically when all processing
associated with the callback is complete.

Enabling and Disabling Operators
You can enable or disable most of the operators in the ELT chain using the following
functions:
void enableHistogram(int enable = TRUE);
void enableConv(int enable=TRUE);
void enableLut(int enable=TRUE);

You can make sure the operators are enabled using the following functions:
void isHistEnabled();
void isConvEnabled();
void isLutEnabled();

380

Setting Operator Values

In addition to enabling and disabling operators, you can set their values.

Setting Operator Values
Before you enable any of the operators in an ELT chain, you must specify parameters for
them. For several of the operators, you can only set their values by using ilELTImg
member functions, such as setConv(). Other operator values can be set using the
ilELTImg constructor.
ilELTImg(ilImage* img=NULL, float minZoom=0.5,
float maxZoom=8.0,ilWarp_2d* warp=NULL, int isR0=TRUE,
void* rsetInfo=NULL);

In the constructor, img represents the input image; generally, this is the R0 image.
minZoom and maxZoom specify the minimum and maximum values of the zoom
range.These parameters often correspond to the range of images in the R-set. The warp
argument specifies the two-dimensional maps used to dewarp the image from the 1.0X
plane coordinates to the R-set’s image plane coordinates. The isR0 argument specifies
whether or not the image is the R0 image. Finally, the rsetInfo is a pointer to user-specific
R-set image data. ilELTImg’s member function, getRsetInfo() can use this information.

Understanding Accelerated Performance
The ELT takes advantage of three processes to accelerate image manipulation processing:
•

look-ahead algorithms

•

hardware implementation of operator functions

•

reprocess the image only when there is a change in operator value

This section looks at each of these processes.

Look-ahead Algorithms
ELT implements look-ahead algorithms that accelerate the roaming and zooming
operations carried out in the ELT chain. The ELT uses one extra page border surrounding
the display window for look-ahead processing. Internally, texture memory, used for
intermediate buffers, is allocated in powers of two. For example, if a 1K by 1K space is

381

Appendix G: Using the Electronic Light Table

allocated as the intermediate, internal buffer, the maximum windows size is 896 by 896
because the look-ahead page border takes up 128 pixels (2 X 64) in each dimension.
You can enable or disable the look-ahead mechanisms using the following functions:
void enableRoamLookAhead(int enable = TRUE);
void enableZoomLookAhead(int enable = FALSE);

By default, the roaming look-ahead mechanism is enabled and the zooming look-ahead
mechanism is disabled. The default values are appropriate if the user will roam but not
zoom. If the user will zoom without roaming, it is better to enable the zoom look-ahead
and disable the roam look-ahead mechanism. If the user will zoom and roam, both
look-ahead mechanisms should be enabled. In this case, however, the performance of
one look-ahead mechanism may suffer because of the processing of the other look-ahead
mechanism.
You can make sure the look-ahead algorithm is enabled by using the following functions:
void isRoamLookAheadEnabled();
void isZoomLookAheadEnabled();

Hardware Acceleration
You can use an ELT application to view and manipulate an image in real time partly
because the manipulation processing is carried out in specialized hardware. You can turn
off this functionality using the following function, however, the performance of your ELT
application will be severely impacted.
void enableFastPath(int enable=TRUE) {fastPath=enable;
setAltered();}

You can make sure the hardware implementation is enabled by using the following
function:
int isFastPathEnabled();

Image Size
Although there is no size limit for the input image, images are stored and processed
internally in 64 X 64 tiles.

382

Choosing a Display in ELT Applications

Images are most often saved in multiple magnifications. Together, all the images at
different magnifications are called a Reduced Resolution Data Set, or R-set. If the original
image is named R0, R1 is generally R0 minimized two times; R2 is R1 minimized two
times, and so on. Each of these minimizations can be filtered for optimal results. When
the user zooms from one magnification to another, the ELT operator actually chooses the
appropriate R-set to input to the ELT pipeline. R-sets often contain seven different
magnifications of the original image.

Choosing a Display in ELT Applications
The output of the ilELTImg operator can be attached to an ilView object to display a
single image, multiple images, or a stereo display. Single and multiple image displays are
implemented using ilDisplay and ilView objects. Stereo views are implemented using
ilStereoView, a derived class of ilView, on RealityEngine and InfiniteReality platforms.
ilStereoView renders left images to the left buffer and right images to the right buffer. The
images are displayed alternately to create a three-dimensional image when viewed
through special glasses.

Creating an ELT Application
The software distribution includes a full-blown example of an ELT application called
ilChain in /usr/share. This section presents a simplified example of an ELT application. It
is based on the example code, ilrzview.c++, found in /usr/share.
This example implements the following user interface:
•

Dragging with the left mouse button moves the view in the display.

•

Dragging with the center mouse button moves the image in the view.

•

Using the left and right arrows on the keyboard rotate the image.

•

Using the up and down arrows on the keyboard zoom the image.

Example G-1 uses the following steps to implement an ELT application:
1.

Parse the command line arguments.

2. Open an image file.

383

Appendix G: Using the Electronic Light Table

3. Instantiate an ilELTImg object.
4. Create an X connection and open a display.
5. Create an X window viewer.
6. Implement the user interface.
Example G-1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

Coding an ELT Application














void
main (int argc, char* argv[])
{
// Step 1: Process the command line arguments
int usage = 0, sizePres = FALSE, attr = 0, autoAbort = FALSE;
int compSize = 8;
int blur = FALSE;
iflSize winsize;
ilResampType resamp = ilBiLinear;
int c;
while ((c = getopt(argc, argv, “bnlcads:”)) != -1) {
switch (c) {
case ‘b’:
blur = TRUE;
break;
case ‘n’:
resamp = ilNearNb;
break;
case ‘l’:
resamp = ilBiLinear;
break;

384

Creating an ELT Application

case ‘c’:
resamp = ilBiCubic;
break;
case ‘a’:
autoAbort = TRUE;
break;
case ‘d’:
compSize = 4;
attr |= ilVisDoubleBuffer;
break;
case ‘s’:
sizePres = TRUE;
sscanf(optarg, “%d,%d”, &winsize.x, &winsize.y);
break;
case ‘?’:
usage = 1;
break;
}
}
if (usage || argc-optind != 1) {
printf(“autoAbort, doubleBuffer, Size\n”);
printf(“%s [-ad -s ] \n”,
argv[0]);
exit(0);
}
// Step 2: Open an image file.
ilFileImg img(argv[optind]);
if (img.getStatus() != ilOKAY) {
char buf[400];
rintf(stderr, “Couldn’t open image file %s: %s\n”, argv[optind],
ilStatusToString(img.getStatus(), buf, sizeof(buf)));
exit(0);
}

// Step 3: Instantiate an ilELTImg object.
ilELTImg myELT(image, .5, 8));
// Step 4: Get an X connection and open a display.
if (!sizePres) img.getSize(winsize, iflLowerLeftOrigin);

385

Appendix G: Using the Electronic Light Table

ilImage* image = &img;
if (blur) image = new ilBlurImg(image);
// Get display connection and clamp window size
Display* dpy = XOpenDisplay(NULL);
int screen = DefaultScreen(dpy);
winsize.x = iflMin(winsize.x, DisplayWidth(dpy, screen));
winsize.y = iflMin(winsize.y, DisplayHeight(dpy, screen));
// Step 5: Create an X window viewer.
ilViewer viewer(dpy, winsize.x, winsize.y, attr, compSize);
if (autoAbort) {
viewer.enableQueueing();
viewer.enableAutoAbort();
}
ilView* view = viewer.addView(&myELT, ilClip|ilCenter);
view->setAutoCenter();
// Step 6: Implement the user interface.
int done=FALSE;
float zoom=1;
int angle=0;
int movieRunning = FALSE;
while (!done) {
XEvent e;
if (movieRunning) {
if (!XCheckWindowEvent(dpy, viewer.getWindow(), -1, &e)) {
int z = view->getZ() + 1;
view->setZ(z);
viewer.paint();
continue;
}
}
else
XNextEvent(dpy, &e);
switch (e.type) {
case KeyPress:

386

Creating an ELT Application

switch(XLookupKeysym(&e.xkey, 0)) {
// center the view in the viewer
case XK_Home:
viewer.display(NULL, ilCenter|ilClip);
break;
// control-Q and escape exit the program
case XK_q:
if (!(e.xkey.state&ControlMask))
break;
/*FALLTHROUGH*/
case XK_Escape:
done = TRUE;
break;
case XK_s:
movieRunning = FALSE;
break;
// flip the image
case XK_h:
viewer.abort();
myELT.setHorizontalFlip(!myELT.getHorizontalFlip());
viewer.paint();
break;
case XK_v:
viewer.abort();
myELT.setVerticalFlip(!myELT.getVerticalFlip());
viewer.paint();
break;
// zoom and rotate the image
case XK_Up:
viewer.abort();
myELT.setZoom(zoom *= 1.2);
viewer.paint();
break;
case XK_Down:
viewer.abort();
myELT.setZoom(zoom /= 1.2);
viewer.paint();
break;
case XK_Right:
viewer.abort();
myELT.setAngle(angle -= 15);
viewer.paint();
break;
case XK_Left:

387

Appendix G: Using the Electronic Light Table

viewer.abort();
myELT.setAngle(angle += 15);
viewer.paint();
break;
}
break;
case DestroyNotify:
viewer.destroyNotify();
done = TRUE;
break;
default:
viewer.event(&e);
break;
}
}
}

Understanding the ilELTImg API
The ilELTImg class has an extensive set of methods. The ilELTImg manpage contains an
extended discussion of each method. Table G-1 summarizes each method.
Table G-1

Methods in ilELTImg

Method

Description

ilELTImg

ilELTImg(ilImage* img=NULL, float minZoom = 0.5,
float maxZoom = 8.0,ilWarp* warp = NULL,
int isR0 = TRUE, void* rsetInfo = NULL)
Constructor for the class.

addRset

ilELTrset* addRset(ilImage* img, float minZoom,
float maxZoom, ilWarp* warp, int isR0 = FALSE,
void* rsetInfo = NULL)
Allows the user to specify additional R-sets for
roaming.

enableConv

void enableConv(int enable=TRUE)
Enables or disables the convolution operation on an
ilELTImg object.

388

Understanding the ilELTImg API

Table G-1 (continued)

Methods in ilELTImg

Method

Description

enableFastPath

void enableFastPath(int enable=TRUE)
Enables or disables the special-purpose hardware
acceleration for ELT.

enableHistogram

void enableHistogram(int enable = TRUE)
Enables or disables the auto histogram operation on
an ilELTImg object.

enableLut

void enableLut(int enable=TRUE)
Enables or disables the table look-up operation on an
ilELTImg object.

enableRoamLookAhead

void enableRoamLookAhead(int enable = TRUE)
Enables or disables the roaming look-ahead operation
on an ilELTImg object.

enableRset

ilStatus enableRset(ilELTrset* rset, int enable = TRUE)
Enables or disables a selected R-set to be in effect.

enableZoomLookAhead

void enableZoomLookAhead(int enable = TRUE)
Enables or disables the zooming look-ahead
operation on an ilELTImg object.

mapFromInput

void mapFromInput(float& u, float& v, float& w,
float x, float y, float z)
Given a point (x, y, z) in the R0 image plane
(regardless of which R-set is currently being roamed),
compute (u, v, w) in the display plane using the
mapping specified for geometric transformation.

mapToInput

void mapToInput(float& x, float& y, float& z, float u,
float v, float w)
Given a point (u, v, w) in the display plane, compute
(x, y, z) in the R0 image plane (regardless of which
R-set is currently being roamed) using the mapping
specified for geometric transformation (including
both the dewarp and the affine-transformation
functions).

389

Appendix G: Using the Electronic Light Table

Table G-1 (continued)

Methods in ilELTImg

Method

Description

getAngle

float getAngle() const
Returns the angle of rotation (in degrees) specified for
the view.

getBBoxCallback

ilCallback* getBBoxCallback() const
Returns the user-provided callback which is used to
compute the bounding box of a given output page
mapped in its input image space.

getBicubicFamily

void getBicubicFamily(float& b, float& c) const
Returns the B and C terms, defining the cubic
resampling coefficients, in b and c.

getConvBias

double getConvBias() const
Returns the current additive bias specified for
convolution as a double.

getConvKernel

const ilKernel* getConvKernel() const
Returns the current convolution kernel.

getEnabled

int getEnabled()
The returned value is comprised of one or more of the
following bit fields:
ilELTImg::ilEPdewarp is set if dewarp operation is
enabled.
ilELTImg::ilEPhist is set if auto histogram operation is
enabled.
ilELTImg::ilEPconv is set if convolution is enabled.
ilELTImg::ilEPlut is set if table look-up is enabled.
ilELTImg::ilEProam is set if roaming look-ahead is
enabled.
ilELTImg::ilEPzoom is set if zooming look-ahead is
enabled.

getHistNbins

int getHistNbins() const
Returns the number of bins currently being specified
for the histogram table.

390

Understanding the ilELTImg API

Table G-1 (continued)

Methods in ilELTImg

Method

Description

getHistPeriod

int getHistPeriod() const
Returns the number of frames between look-up table
updates; default is 30 frames.

getHistCallback

ilCallback* getHistCallback() const
Returns the current histogram callback being used or
NULL if none has been defined.

getLookUpTable

const iflLut* getLookUpTable() const
Returns the current lookup table being provided by
the user.

getMaxMeshSize

int getMaxMeshSize() const
Returns the maximum allowable mesh size used for
geometric transformations.

getPairedImg

ilELTImg* getPairedImg() const
Returns the other image of a paired image, for
example, in a stereo image.

getResampType

ilResampType getResampType() const
Returns the current resampling type used in
geometric transformations.

getRset

ilELTrset* getRset(ilImage* img) const
Returns an opaque handle to the R-set for the input
image, img.

getRsetChangeCallback

ilCallback* getRsetChangeCallback() const
Returns the user-provided callback which indicates
when the input image to the ELT has switched from
one R-set to another.

getRsetInfo

void* getRsetInfo(ilELTrset* rset) const
Returns the user-provided information about the
specified R-set. If no R-set information has been
provided, NULL is returned.

getRsetZoomRange

ilStatus getRsetZoomRange(ilELTrset* rset,
float& minZoom, float& maxZoom)
Gets the scaling range covered by the specified R-set.

391

Appendix G: Using the Electronic Light Table

Table G-1 (continued)

Methods in ilELTImg

Method

Description

getTmeshCallback

ilCallback* getTmeshCallback() const
Returns the user-provided callback which a generates
triangle mesh for a given output page.

getWarp

ilStatus getWarp(ilELTrset* rset, const ilWarp*& func)
const
Returns rset’s dewarp function in func.

getZoom

float getZoom() const
Returns the current display scale being specified.

isConvEnabled

int isConvEnabled()
Returns TRUE if convolution is enabled for the
current operation; FALSE, otherwise.

isFastPathEnabled

int isFastPathEnabled()
Returns TRUE if special purpose hardware
acceleration is enabled for the current operation;
FALSE, otherwise.

isHistEnabled

int isHistEnabled()
Returns TRUE if auto histogram is enabled for the
current operation; FALSE, otherwise.

isLutEnabled

int isLutEnabled()
Returns TRUE if table look-up is enabled for the
current operation; FALSE, otherwise.

isRoamLookAheadEnabled int isRoamLookAheadEnabled()
Returns TRUE if roaming look-ahead is enabled for
the current operation; FALSE, otherwise.
isZoomLookAheadEnabled int isZoomLookAheadEnabled()
Returns TRUE if zooming look-ahead is enabled for
the current operation; FALSE, otherwise.
removeRset

ilStatus removeRset(ilELTrset* rset)
Removes the specified R-set from the roaming
operation.

392

Understanding the ilELTImg API

Table G-1 (continued)

Methods in ilELTImg

Method

Description

setAngle

void setAngle(float ang)
Changes the rotation angle to that specified by the
argument, ang, which is specified in degrees.

setBBoxCallback

void setBBoxCallback(ilCallback* callback = NULL)
Sets up a callback to compute the bounding box of a
given output page mapped in its input image space.

setBicubicFamily

void setBicubicFamily(float b=1., float c=0.)
Specifies the B and C terms which define the bicubic
resampling coefficients.

setConvBias

ilStatus setConvBias(double biasVal)
Sets the additive bias applied to all pixels after the
convolution operation.

setConvKernel

void setConvKernel(ilKernel* inputKernel,
int doClamp=TRUE)
Sets the convolution kernel.

setHistNbins

ilStatus setHistNbins(int num)
Sets the number of bins in the histogram table.

setHistPeriod

ilStatus setHistPeriod(int numFrames)
Sets the number of frames between LUT updates.

setHistCallback

ilStatus setHistCallback(ilCallback* callback =
NULL)
Provides a histogram callback.

setLookUpTable

ilStatus setLookUpTable(const iflLut& lut)
Sets a new look-up table to be downloaded to the
graphics system.

setMaxMeshSize

void setMaxMeshSize(int N)
Sets the maximum allowable mesh size for geometric
transformation to be NxN pixels.

setPairedImg

void setPairedImg(ilELTImg* pairedImg)
Sets the other image of an image pair (i.e., stereo pair).

393

Appendix G: Using the Electronic Light Table

Table G-1 (continued)

Methods in ilELTImg

Method

Description

setResampType

ilStatus setResampType(ilResampType rs)
Selects the resampling type to be used.

setRsetChangeCallback

void setRsetChangeCallback(ilCallback* callback =
NULL)
Provides a R-set change callback.

setRsetInfo

ilStatus setRsetInfo(ilELTrset* rset, void* rsetInfo =
NULL)
Sets the user-specified R-set information for the given
R-set handle.

setRsetZoomRange

ilStatus setRsetZoomRange(ilELTrset* rset,
float minZoom, float maxZoom)
Sets the scaling range covered by the specified R-set.

setTmeshCallback

void setTmeshCallback(ilCallback* callback = NULL)
Provides a triangle mesh generation callback.

setTmeshDelCallback

void setTmeshCallback(ilCallback* callback = NULL)
Sets up a callback to clean up triangle lists after the
system finishes drawing them.

setWarp

ilStatus setWarp(ilELTrset* rset, const ilWarp* func)
Sets rset’s dewarp function to be func.

setZoom

void setZoom(float scale)
Changes the display scale to scale.

394

Appendix H

H. Results of Operators

This appendix presents examples in the following sections of all the operators that give
visible results:
•

“Color Conversion” on page 396

•

“Arithmetic and Logical Transformations” on page 397

•

“Geometric Transformations” on page 400

•

“Spatial Domain Transformations” on page 401

•

“Edge Detection” on page 402

•

“Frequency Domain Transformations” on page 404

•

“Radiometric Transformations” on page 405

•

“Combining Images” on page 407

For more information on using these operators and what effect they have on image data,
see Chapter 4, “Operating on an Image.” More specific information on how to apply each
operator is located in its header file and in its reference page.
Original, unprocessed images are presented where necessary. Some images combine two
original images. These images are either reversed copies of the same image or two
extremely similar images.

395

Appendix H: Results of Operators

Color Conversion

Figure H-1

ilFalseColorImg

.

Figure H-2

396

ilGrayImg

Arithmetic and Logical Transformations

Arithmetic and Logical Transformations

Figure H-3

Original Image and Flipped Image

Figure H-4

ilAddImg and ilAndImg

Original

Figure H-5

Square Root of Original

Original and Square Root Divided

ilDivImg

397

Appendix H: Results of Operators

398

Figure H-6

ilExpImg and ilInvertImg

Figure H-7

ilLogImg and ilMaxImg

Figure H-8

ilMinImg and ilMultiplyImg

Arithmetic and Logical Transformations

Figure H-9

ilNegImg and ilOrImg

Figure H-10

ilPowerImg and ilSqRootImg

Figure H-11

ilSquareImg and ilSubtractImg

399

Appendix H: Results of Operators

.

Figure H-12

ilXorImg

Geometric Transformations

Figure H-13

400

Original and ilRotZoomImg

Spatial Domain Transformations

.
Figure H-14

ilWarpImg

Spatial Domain Transformations

Figure H-15

Original, ilBlurImg and ilGBlurImg

Figure H-16

ilDilateImg, ilErodeImg, and ilMaxFltImg

401

Appendix H: Results of Operators

Figure H-17

ilMedFltImg, ilMinFltImg, and ilSharpenImg

Edge Detection

.

402

Figure H-18

ilCompassImg

Figure H-19

ilLaplaceImg (original and filtered image)

Edge Detection

Figure H-20

ilRobertsImg (original and filtered image)

Figure H-21

ilSobelImg (original and filtered image)

403

Appendix H: Results of Operators

Frequency Domain Transformations
The frequency domain transformations are of limited interest as illustrations. For the
purposes of this appendix, one example is shown. In the example, an original image is
presented along with its appearance in the frequency, or Fourier domain, and the filtered
resultant image is shown in both the spatial and frequency domains.

Original

Frequency Domain

Filtered Original

Frequency Domain

Figure H-22

404

ilFGaussFiltImg

Radiometric Transformations

Radiometric Transformations

Figure H-23

ilHistEqImg (filtered image and histogram)

Figure H-24

ilHistNormImg (filtered image and histogram)

405

Appendix H: Results of Operators

Figure H-25

ilHistScaleImg (filtered image and histogram)

Figure H-26

ilLutImg (original, filtered image, and LUT editor)

.

Figure H-27

406

ilThreshImg

Combining Images

Combining Images

Figure H-28

Originals and Original Mask

Figure H-29

ilBlendImg

407

Appendix H: Results of Operators

Figure H-30

408

ilCombineImg

Index

Numbers

B

64-bit address space, 289

background
color, 172
view, 172
bias value, 95, 108, 110, 117, 119
BinBin, 116
BinGray, 116
blending images, 146
blurring an image, 109
border style, 173
breakpoint, 143
buffers, using, 253

A
absolute value operator, 92
accessing data, see reading, writing, or copying
addInput(), 59
addition operator, 94
addView(), 168, 177
affine transformations, 105
alignImg(), 188
aligning
images, 188
views, 185, 189
alignView(), 188, 189
allocPage(), 214
alpha value, 147
AND operator, 97
arithmetic operators, 90-97
dual-input, 94-98
single-input, 91
asynchrounous operations, 292
attributes, see image attributes

C
cache, 32-39, 215
monitoring, 251
optimizing use of, 247
replacing pages in, 34
cache size, 248-249
affected by multi-threading, 249
default, 248
optimum, 248
calcPage(), 226, 230, 235
chain, 56

409

Index

chain of operators, 17, 50
components of, 18
propagation, 59
querying, 58
reconfiguring, 57
changes, to 2.5 classes, 298
checkColorModel(), 207
checkMinMax(), 228
C interface, 9
clamp(), 227
classes, new in 3.0, 289
class name conversions, 308
clearAllowed(), 210
clearCenter(), 105
clearSet(), 60
clearStatus(), 22
clipping, 48
clipTile(), 48
closing a file, 342
cmplxVectorCalc(), 238
color conversion, 85-87
color-index mode, 364
color model, 25, 26, 85
determining, 26, 87
initializing when deriving, 203
color palette, 27
color saturation, 89
combining images, 146
compass operator, 118
compatibility with version 2.5, 306
compiling, 273
complex conjugate of an image, 130
compose(), 360
compression, data, 32, 75
conjugate of an image, 130
constants, 369

410

constant value image, 152
convolution, 108, 117, 132
coordinates, 365
initializing, 196
copying
image data, 40
tiles efficiently, 252
copyTile(), 40-44, 52, 74, 105, 252, 253
copyTile3D(), 46
copyTileCfg(), 44, 46
creating a file, 73
cross-correlation operator, 131

D
data ordering, 24-25
data type of an image, 23, 26, 363
deferred drawing, 174, 177, 191
deleteView(), 180
deriving classes, 199
dilation, 113
display(), 184, 185, 186
display facility in 3.0, 294
displaying an image, 159
display mode, 175
display operators, 184
division by zero, 95, 132
division operator, 94
for Fourier images, 131
doUserPageAlloc(), 214
drawing
area, 175
deferred, 174, 177, 191
views, 184

Index

edge detection, 117-119
edge image, 117, 118
edge mode, 107, 110, 113, 118, 119
enableMP(), 133
enumerated types, 369
for image attributes, 370
environment variable, 278
erosion, 113
error codes, 22, 366
error handling in 3.0, 295
evalUV(), 50, 104
evalXY(), 49, 104
event-handling, 168
exclusive-OR operator, 97
executeRequest, 213
execution model, 50-59
advantages, 51
exponential operator, 92
Fourier, 131
exporting data, 78
extending the IL, 199

filename syntax for images, 72
fillTile(), 41-46
fillTile3D(), 47
fill value, 29, 108
findEdge(), 181, 194
findPoint(), 145
findView(), 181
finishRequest, 213
FIT, 6
FIT file format, 66, 69
flags, for display operators, 175
align mode, 176
coordinate, 176
display, 176
wipe mode, 176
flipping an image, 105
flush(), 36, 214
Fourier filtering, 126
Fourier transform, 120
freePage(), 214
freqFilt(), 241
frequency domain operators, 120-132
frequency filtering, 126

F

G

FALSE, 369
faLse coloring, 88
fast Fourier transform, 120
features, new for 3.0, 288
file
access mode, 72
closing, 342
creating, 73
opening, 71
file formats, 66

Gaussian kernel, 109
geometric operators, 98-106
getAddressError(), 102
getAlphaRange(), 149
getAngle(), 105, 120
getBackground(), 172
getBias(), 119
getBkgd(), 154
getBorderStyle, 173

E

411

Index

getBreakpoints(), 145
getCacheSize(), 36
getCenter(), 105
getChained(), 59
getClassProp(), 63
getCoeff(), 102
getColorMap(), 27
getColorModel(), 25
getCompression(), 76
getCopyConverter(), 207
getCsize(), 23
getCurrentImg(), 68, 69
getDataPtr(), 80
getDataType(), 23
getDBinSize(), 134
getDMax(), 135
getDMean(), 135
getDMin(), 135
getDStart(), 134
getDStDev(), 135
getEdgeMode(), 107
getErrorColor, 177
getFileDesc(), 76
getFileMode(), 77
getFileName(), 76
getFill(), 29
getFloatProp(), 62
getHist(), 134
getImg(), 179
getInput(), 59
getInputMax(), 228
getInputMin(), 228
getIntProp(), 61
getKernelSize(), 107
getLImg(), 179

412

getLoc(), 183
getMaxPixel(), 30
getMaxRank(), 113
getMaxValue(), 31, 115
getMinPixel(), 30
getMinValue(), 31
getMorphType(), 117
getNbins(), 134
getNumBreakpoints(), 145
getNumChained(), 59
getNumImgs(), 68, 69, 77
getNumInputs(), 59
getNumViews(), 179
getOffset(), 94
getOrder(), 24
getOrientation(), 28, 155
getOrigin(), 107
getPage(), 52, 227
overriding when deriving, 345
getPageBorder(), 56
getPageSize(), 38
getPageSizePix(), 39
getPageSizeVal(), 39
getPixel(), 41, 45, 182
getPoint(), 145
getPolyOrder(), 102, 104
getProp(), 62
getPropSet(), 63
getPtrProp(), 62
getResampType(), 102
getRImg(), 179
getRoi(), 151, 154
getSaturation(), 90
getScaleMax(), 31
getScaleMin(), 31

Index

getSize(), 21, 23, 49, 175
getStart(), 158, 196
getStatus(), 22, 366
getStrides(), 47
getStrides3D(), 214
getSubTile(), 41-45
getSubTile3D(), 46, 153
getThresh(), 142
getTile(), 34, 40-43, 52, 105
getTile3D(), 46
getTotal(), 135
getValidValue(), 156
getView(), 179
getViewIndex(), 179
getVisibleArea, 164
getXImg(), 179
getXkernel(), 109
getXsize(), 23
getXYWt(), 120
getYkernel(), 109
getYsize(), 21, 23
getZ(), 178
getZoom(), 105
getZsize(), 23
GIF, 6
GIF file format, 66
gradient operators, 117
GrayBin, 116
GrayGrayFct, 116
GrayGraySet, 116

H
hardware acceleration, 55
disabling, 56

hasPages(), 39, 158
header files, 5
including, 11
high-pass filter, 128
histogram
equalization, 140
of an image, 132, 134, 136
operators, 139
scaling, 140

I
IFL, 293
iflABGR, 25
iflBGR, 25
iflBitArray, 358
iflCMY, 25, 87
iflCMYK, 25
iflColorModel, 25
iflConfig, 358
iflDataIsSigned(), 364
iflDataSize(), 73, 221, 363
iflDataType, 23
iflDataTypeFromRange(), 363
iflFile, 338
iflHSV, 25
iflInterleaved, 24
iflLut, 27, 358, 360-362
iflMax(), 364
iflMin(), 364
iflMinBlack, 25
iflMinWhite, 25
iflMultiSpectral, 25
iflOrientation, 28, 48
iflPixel, 29, 154, 358
iflRGB, 25

413

Index

iflRGBA, 25, 87
iflRGBPalette, 25
iflSeparate, 24
iflSequential, 24
iflSize, 22, 358
iflTile, 359
iflXYSfloat, 103
iflXYZCint, 219, 235
iflYCC, 25, 87
IL_ARENA_MAXUSERS, 278
IL_CACHE_FRACTION, 35, 278
IL_CACHE_SIZE, 35, 278
IL_COMPUTE_THREADS, 54, 278
IL_DEBUG, 278
IL_HW_ACCELERATE, 278
IL_MONITOR, 279
IL_MONITOR_CACHE, 251, 279
IL_MONITOR_COMPACTION, 279
IL_MONITOR_LOCKS, 279
IL_MP_ARENA_SIZE, 279
IL_MP_LOCKS, 279
IL_SPARE_THREADS, 54
ilABGRImg, 86
ilAbsImg, 92
ilAbsSplit, 191
ilAddImg, 94
color illustration, 96, 397
ilAndImg, 97
color illustration, 397
illustration, 98
ilArithLutImg, 92
ilBiCubic, 100
ilBiLinear, 100
ilBitMapRoi, 155
ilBlendImg, 146, 147-150
color illustration, 407

414

illustration, 148
ilBlurImg, 109
color illustration, 111, 401
ilBRG, 87
ilBuffer, 358
ilCacheImg
deriving from, 201-215
ilCMYKImg, 86
ilColorImg, 86
ilColSplit, 191
ilCombineImg, 146, 151
color illustration, 408
ilCompactCache(), 36
ilCompassImg, 118
illustration, 120, 402
ilConfig, 21, 41, 157, 359-360
ilConstImg, 91
ilConvImg, 108, 132
ilDATACLIPPED, 48
ilDelVal, 188
ilDilateImg, 116-117
illustration, 401
ilDilateImg(), 116
ilDisplay, 16, 162
creating, 169
ilDisplayImg, 162
ilDivImg, 94
color illustration, 397
ilDumpChain(), 251
ilDyadicImg, 90
deriving from, 228-230
ilEdgeMode, 107
ilErodeImg, 116-117
illustration, 401
ilExpImg, 92
color illustration, 398

Index

ilFalseColorImg, 88-89
color illustration, 89, 396
ilFConjImg, 130
ilFCrCorrImg, 131
ilFDivImg, 131
ilFDyadicImg, 131
deriving from, 238-241
ilFExpFiltImg, 126
ilFFiltImg, 126
deriving from, 241
ilFGaussFiltImg, 126
color illustration, 404
illustration, 129
ilFileImg, 19
ilFlushCache(), 36
ilFMagImg, 125
ilFMergeImg, 126
ilFMonadicImg, 130
deriving from, 238-241
ilFMultImg, 131
ilFPhaseImg, 125
ilFRaisePwrImg, 130
ilFrameBufferImg, 19
ilFSpectImg, 125
ilGBlurImg, 109
color illustration, 401
ilGetCompactFraction(), 35
ilGetCurCacheSize(), 35
ilGrayImg, 86
illustration, 396
ilHistEqImg, 136, 140
color illustration, 405
ilHistLutImg, 139
ilHistNormImg, 136
color illustration, 405
ilHistScaleImg, 126, 136, 140
color illustration, 406

ilHSVImg, 87
ilImage, 19
deriving from, 200-211
ilImgParam, 60, 210
ilImgStat, 132, 136, 140
ilInvertImg, 92
color illustration, 398
ilKernel, 107, 358
ilLaplaceImg, 118
color illustration, 402
ilLink
classes deriving from, 16
implements chaining model, 18
ilLockRequest, 39
ilLogImg, 92
color illustration, 398
ilLutImg, 142
color illustration, 406
ilMaxFltImg, 113
color illustration, 401
ilMaxImg, 96
color illustration, 398
ilMedFltImg, 113
color illustration, 114, 402
ilMemCacheImg, 19, 33
deriving from, 213-215
ilMemoryImg, 19, 78
ilMergeImg, 146, 151
ilMinFltImg, 113
color illustration, 402
ilMinify, 100
ilMinImg, 96
color illustration, 97, 398
ilMonadicImg, 90, 137
deriving from, 219, 227, 228-230
ilMorphType, 116

415

Index

ilMultiplyImg, 94
color illustration, 398
ilNearNb, 100
ilNegImg, 92
color illustration, 93, 399
ilNoPad, 108
ilOKAY, 22
ilOldRel, 188
IlOpImg, 19
ilOpImg, 82-83
deriving from, 201, 215-242
ilOpImg, 3.0 changes, 293
ilOrImg, 97
color illustration, 399
illustration, 98
ilPackSplit, 191
ilPadDst, 108
ilPadSrc, 108
ilPage, 358
ilPiecewiseImg, 143-146
ilPixel, 30, 182
ilPowerImg, 92
color illustration, 399
ilRankFltImg, 113
ilRectRoi, 155
ilReflect, 107
ilRelSplit, 191
ilRelVal, 188
ilResampType, 100
ilRFFTfImg, 120, 126, 130, 131-132
ilRFFTiImg, 120, 126, 130, 131-132
ilRGBImg, 87
ilRobertsImg, 117
color illustration, 118, 403
ilRoi, 133
deriving from, 201, 242

416

ilRoiImg, 153, 154
ilRotZoomImg, 7, 98-106
color illustration, 400
ilRowSplit, 191
ilSaturateImg, 89-90
ilScaleImg, 137
color illustration, 139
ilSepConvImg, 108, 109
ilSetCompactFraction(), 35
ilSetMaxCacheFraction(), 35, 248
ilSetMaxCacheSize(), 35, 248
ilSGIPaletteImg, 87
ilSharpenImg, 7, 111
color illustration, 112, 402
ilSigned(), 24
ilSobelImg, 117
color illustration, 403
ilSpatialImg, 107, 117
deriving from, 234-236
ilSqRootImg, 92
color illustration, 399
ilSquareImg, 92
color illustration, 399
ilSquareRootImg
color illustration, 397
ilStackAlloc, 358
ilStatus, 22
ilSubImg, 153, 156
ilSubtractImg, 94
color illustration, 399
ilThreshImg, 141, 168
color illustration, 406
ilTieWarpImg, 98, 103
color illustration, 104
ilUserDef, 100
ilView, 162, 177, 179
ilViewBdrCornerHandles, 173

Index

ilViewBdrDashedLines, 173
ilViewBdrMiddleHandles, 173
ilViewBdrSolidLines, 173
ilViewBorderStyle, 173
ilViewer, 8
ilWarpImg, 98, 99
color illustration, 401
ilWrap, 107
ilXorImg, 97
color illustration, 400
image
aligning, 188
moving, 190
replacing, 180
retrieving, 179
image attributes, 20, 60, 370
adding new, 211
allowing to change, 208
clearing once set, 60
initializing when deriving, 202
marking as altered or set, 210
preventing from changing, 60, 210
propagating, 59
resetting, 208
setting directly when deriving, 211, 221
image chains
constructing dynamically, 57
querying, 58
replacing an operator in, 57
image format, 21
Image Format Library, 293
Image Pac, 68
image tools, 277
imgcopy tool, 277
imgformats tool, 277
imginfo tool, 277
imgview tool, 277
importing data, 78

initClamp(), 227
initColorModel(), 203
initMinMax(), 202
initScaleMinMax(), 31
insertPoint(), 144
inset, 194
interleaved ordering, 24
invert(), 360
isAltered(), 210
isDefer(), 175
isDiff(), 221
isInvertable(), 360
isMirrorSpace(), 49
isMPenabled(), 133
isNop(), 174
isSet(), 211
isSigned(), 364
isStaticUpdate(), 187
isWellDefined(), 103

J
JFIF, 6
JFIF file format, 67
JPEG file format, 67

K
kernel, 106, 107, 108, 110, 113, 117, 118, 119, 235, 358
separable, 109

417

Index

L
Laplace operator, 118
laying out views, 191
left-shift operator, 44
linking with libraries, 274
loadLut() example, 232
lockPage(), 37, 39, 250
lockPageSet(), 39
logical operators, 90-97
log operator, 92
lookup table, 27, 358
low-pass filter, 128, 241
LUT, see lookup table

M
magnifying an image, 105
magnitude component, Fourier image, 124
Makefile, 274
mapChan(), 360
mapFlipTrans(), 49
mapFromSource(), 49
mapSpace(), 49
mapTile(), 49
mapToSource(), 49
mapXY(), 49
mapXYSign(), 49
markSet(), 211
masking, 153
maximum comparison, 96, 364
maximum filtering, 113
maximum pixel value, 30, 132, 135, 141, 228
initializing when deriving, 202
mean and standard deviation, 132, 135, 140

418

median filtering, 113
memory
optimizing usage, 247-253
memory image, 78
merging images, 146
minifying an image, 105
minimum comparison, 96, 364
minimum filtering, 113
minimum pixel value, 30, 132, 135, 141, 228
initializing when deriving, 202
mirroring an image, 105
mode, display, 175
monitoring cache, 251
morphological
dilation, 113
erosion, 113
operators, 114-117
moveImg(), 188, 190, 196
moveView(), 188
moving
images, 190
views, 191
multiplication operator, 94
for Fourier images, 132
multispectral image, 25, 88
multi-threading, 53, 227
effect on cache size, 249
turning off, 54
multi-threading architecture, 292

N
needColorConv(), 207
nop flag, 174
NULL, 369

Index

O
object properties, 61
scope of, 61
one’s complement operator, 92
online
documentation, 275
source code, 277
OpenGL, 171
opening a file, 71, 168
operator, definition of, 17
order of an image, 24
orientation, 28, 48
OR operator, 97
outOfBound(), 206
overflow, 227

P
padding an image, 108, 156
page
default size, 74
replacement, 34
size, 38, 251, 252
pageBorder, 214
page borders, 56
pages
priority in cache, 36
pageSize, 214
pageSizeBytes, 214
paging support, 39
paint(), 184, 186
PBM file format, 69
PCD, 6
PCDO, 6
PGM file format, 69

phase component, Fourier image, 124
Photo CD
color model, 68
file format, 67
Image Pac, 68
image resolutions, 68
Overview Pac, 69
pixel
operating on, 182
PNG file format, 69
pop(), 180
position(), 191
power operator, 92
PPM, 6
PPM file format, 69
prepareRequest, 213
priority, of cache pages, 250
priority of pages in cache, 36
propagating image attributes, 59
push(), 180

R
radiometric operators, 136
rank filtering, 113
Raw, 6
Raw image file format, 69
reading image data, 40
redraw(), 52, 185, 187
reference pages, 275
region of interest, 105, 133, 136, 140, 153-158
bitmap, 155
combining images with, 151
rectangular, 155
removeInput(), 59
removePoint(), 144

419

Index

removeProp(), 62
rendering, 171, 286
replacePoint(), 144
resampling method, 100, 105
reset(), 135, 208
resetCheck(), 208, 218
resetDomain(), 138
reset mechanism, 206, 218
resetOp(), 208, 217-221, 228
example, 220
resetRange(), 138
resetScaling(), 138
resize(), 193
Roberts operator, 117
ROI, see region of interest
root-filtering, 131
rotating an image, 7, 105
run-time object-type inquiries, 297

S
save(), 185
scaling data
during color conversion, 31
for displaying, 228
scrolling window, 164
select(), 172
separable kernel, 109
separate ordering, 25
sequential ordering, 24
setAddressError(), 102
setAllowed(), 208
setAlpha(), 130
setAlphaPlane(), 149
setAlphaRange(), 149

420

setAltered(), 210, 218
setAngle(), 105, 120
setAutoStaticUpdate(), 187
setBackground(), 172
setBase(), 93
setBeta(), 130
setBias(), 108, 119
setBicubicFamily(), 100
setBkgd(), 154
setBlendMode(), 149
setBlur(), 110
setBlurKernelSize(), 110
setBlurRadius(), 110
setBorders(), 168, 172
setBorderStyle, 173
setBorderWidth(), 173
setBreakpoints(), 144
setCenter(), 105
setCheck(), 95, 132
setClamp(), 227
setClip(), 141
setCoeff(), 102
setColorMap(), 27
setColorModel, 26
setColorModel(), 25
setConfig(), 157
setConstAlpha(), 149
setCsize(), 21, 23
setCurrentImg(), 68, 69
setDataType(), 24, 38
setDCgain(), 130
setDefer(), 174
setDomain(), 138
setEccent(), 130
setEdgeMode(), 107

Index

setErrorColor, 177
setFill(), 29
setGamma(), 130
setHFgain(), 130
setHistLimits(), 141
setImg(), 180
setImgStat(), 140, 141
setInput(), 58, 219
setKernel(), 107, 118
setKernelSize(), 107
setKernFlags(), 235
setLoc(), 183
setLookUpTable(), 143
setMajHalf(), 130
setMaxComputeThreads, 54
setMaxPixel(), 30
setMaxSamples(), 102
setMaxValue(), 31
setMean(), 140
setMinHalf(), 130
setMinifyKernel(), 102
setMinPixel(), 30
setMinValue(), 31, 115
setMorphType(), 117
setNop(), 174
setNumInputs(), 59, 203, 219
setOffset(), 94, 153
setOption(), 122, 124
setOrder(), 25, 38
setOrientation(), 28, 155
setOrigin(), 107
setPage()
overriding when deriving, 345
setPageBorder(), 56
setPageSize(), 38

setPagingCallback(), 212, 251
setPixel(), 41, 45, 182
setPolyOrder(), 103
setPower(), 93, 131
setPriority(), 37, 250
setProp(), 62
setRange(), 138, 141
setRank(), 113
setResampFunc(), 102
setResampType(), 102
setRoi(), 140, 141, 151, 154
setSaturation(), 90
setScale(), 130
setScaleMinMax(), 31
setScaleType(), 31
setScaling(), 138
setSharpenRadius(), 112
setSharpness(), 112
setSize(), 21, 23, 94, 105
setStart(), 157, 196
setStaticUpdate(), 185, 187
setStatus(), 203
setStdev(), 140
setSubTile(), 41-45
setSubTile3D(), 46, 153
setTheta(), 130
setThresh(), 142
setTiePoints(), 103
setTile(), 40-43
setTile3D(), 46
setValidOrder(), 219
setValidType(), 219
setValidValue(), 156
setVisibleArea, 164
setWorkingType(), 219

421

Index

setXImg(), 180
setXkernel(), 109
setXsize(), 126
setXYWt(), 120
setYkernel(), 109
setZ(), 178
setZoom(), 105
SGI, 6
SGI file format, 70
sharpening an image, 7, 109
size of an image, 22
sizeToFit, 106
Sobel operator, 117
spatial operators, 106-113
split(), 188, 191
square root operator, 92
squaring operator, 92
standard deviation, 132, 135, 140
statistical operator, 132-135
stereo viewing, 178
stride, 47, 235
subimage, 153, 156
subtraction operator, 94
swap(), 180

T
threshold operator, 141
TIFF, 6
TIFF file format, 70
tile of data, 40
tools, image, 277
TRUE, 369
two’s complement operator, 92

422

U
underflow, 227
unlockPage(), 39
unlockPageSet(), 39
unselect(), 172
update(), 184, 195

V
version 3.0, overview of changes, 287
view
adding, 177
borders, 172
finding an edge, 181
moving, 191, 196
removing, 180
reordering in the stack, 180
resizing, 193
retrieving, 179
stereo, 178
updating, 195
view stack, 177
reordering, 180
viewstack, 162

Index

W
warping operators, 98-106
window, 175
window,scrolling, 164
wipe(), 193
wipeSize(), 193, 194
wipeSplit(), 193, 194
wiping an image, 193
working type, 27
writing image data, 40

X
XNextEvent(), 168
XOpenDisplay(), 169
XQueryPointer(), 169
X rendering, 171, 286

Z
zooming an image, 105

423

Tell Us About This Manual
As a user of Silicon Graphics products, you can help us to better understand your needs
and to improve the quality of our documentation.
Any information that you provide will be useful. Here is a list of suggested topics:
•

General impression of the document

•

Omission of material that you expected to find

•

Technical errors

•

Relevance of the material to the job you had to do

•

Quality of the printing and binding

Please send the title and part number of the document with your comments. The part
number for this document is 007-1387-050.
Thank you!

Three Ways to Reach Us
•

To send your comments by electronic mail, use either of these addresses:
–

On the Internet: techpubs@sgi.com

–

For UUCP mail (through any backbone site): [your_site]!sgi!techpubs

•

To fax your comments (or annotated copies of manual pages), use this
fax number: 650-965-0964

•

To send your comments by traditional mail, use this address:
Technical Publications
Silicon Graphics, Inc.
2011 North Shoreline Boulevard, M/S 535
Mountain View, California 94043-1389



Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.2
Linearized                      : Yes
Create Date                     : 2001:05:15 17:25:19
Producer                        : Acrobat Distiller 4.0 for Windows
Modify Date                     : 2001:05:15 17:25:20-07:00
Page Count                      : 450
EXIF Metadata provided by
EXIF.tools

Navigation menu