Operations
1. The problem of not making the length explicit will be discussed in terms of efficiency in Chapter 8.
3.3 Pointwise operations
3.3.1 D efinition of pointw ise operations in M iranda
In the naive pointwise operations implemented in the previous chapter (Section 2.2), it is assumed that im ages are at the same position (though they can be of different size). But now w e have the representation of images at different positions and pointwise operations between those images can be defined. Unary operations are straightforward as usually the operations will not change the size and position of an image, but binary operations need consideration. How to handle these operations is a matter of discussion but one possible schem e may be to define the result of a binary pointwise operation only where the both source images are defined. This may be reasonable because a binary operation without two operands usually does not have well-defined meaning [Kozato88a].
unarylnterval::(*->**)->interval *->interval ** unarylnterval f (o,p) = (o, map f p)
binarylnterval:;<*->**->***)->interval *->interval **->lnterval *** binarylnterval f (ol,pl) (o2,p2)
= (o, map2 f (drop (o2-ol) pi) p2) , if cl < o2 = (o, map2 f pi (drop (ol-o2) p2)) , otherwise
where o = max2 ol o2
In order to take the common part in a binary operation, a new origin is calculated as the larger of the origins of two source intervals, d ro p takes a number, n, and a list, and returns the p art of the list which rem ains after the first n elements have been removed. map2 is sim ilar to map but takes three arguments: a function of two arguments, f, and two lists. The result is a list in which each element is the result of applying the function f to the corresponding elements in the two lists. If the two lists are of different lengths, the excess portion of the longer list is ignored.
Note that these functions are defined as polymorphic higher-order functions. That is, as long as the second argument (and third one for a binary function) has the pattern of i n t e r v a l , the functions do not care about the content of the list part in the interval. It is the parameterised function, f , w hich cares about the content.
As the d ata structures of i n t e r v a l and row are identical, unary and binary pointwise
operations for rows are:
unaryRow = unarylnterval binaryRow = binarylnterval
(Composition of 2D operations using these ID operations is exactly the same as the naive
version (Section 2.23) and is equivalent to the following definitions using the function
composition operator (.):
unaryPointwise = unaryRow.unaryRow binaryPointwise = binaryRow.binaryRow
These definitions are remarkably simple and easy to write and read because of their conciseness and modularity. Higher-order functions and polymorphic typing are the main contributors. The following subsections give a few example pointwise operations using the higher-order polymorphic pointwise functions defined here.
4 2-
3.3.2 Sim ple p ointw ise exam ples
Picking up several operators and functions from the standard environment, example unaiy pointwise operations can be implemented:
negatelmage = unaryPointwise neg
absImage * unaryPointwise abs
loglmage = unaryPointwise log
constImage x = unaryPointwise (const x)
notImage = unaryPointwise (-)
doublelmage » unaryPointwise (*2)
Pixel operations used in the examples are as follows: n eg does the unaiy minus, a b s takes the absolute value of a number, lo ^ gives the natural logarithm, c o n s t returns a constant value and is defined as:
const X y = X
(~) is logical negation, and the last one (*2) multiplies by two.
And the following are a few example binary pointwise operations: addlmage = binaryPointwise (+)
maxImage = binaryPointwise max2 minlmage = binaryPointwise min2 andlmage = binaryPointwise (£) orlmage = binaryPointwise (\/) eqImage = binaryPointwise (=)
(&) and ( \ / ) are logical AND and OR, and (=) does comparison.
Type signatures can be omitted; Miranda infers the type of each function correctly. For example:
c o n s t l m a g e ; : * - > (num, [ (num,[ * * ] ) ] ) - > (num,[ (num,( * ] ) ] ) notImage:: (num,( (num, [bool ] ) ] ) - > (num,[ ( num, [ bool ] ) ] )
eqi mage: : (num,[ (num,[ * ] ) ] ) - > (num,[ (num,[ * ] ) ] ) - > (num,[ ( num, [bo o l ]) ] )
This shows that various types of images can be handled in a unified manner.
3.3.3 Look-up table functions
In image processing, it is often necessary to apply a fixed transformation to all pixels in an image. For example, gamma correction to compensate for the characteristics of a display
device, or to map pixel values to colour codes for pseudo-colour display. Such operations are commonly called look-up table operations, because the f ix ^ transformation is normally
represented as an array or look-up table. Using the una r y P o in t w ise function defined above,
a look-up table function of an integer image can be implemented by passing a list indexing function as an argument. For example, an inverse look-up table of an 8 bit integer image, whose pixel values range between 0 and 255, can be written as follows:
inverseLookUpTable = unaryPointwise ( [255,254..0]!)
The operator ! is list indexing to look up a specified element in a list. It should be pointed out
that this look-up table works on an 8 bit integer image only, since an index m ust be an integer although the type of ! is defined as [*] ->num->* in Miranda^. It is rath er aw kw ard for certain applications that a language does not differentiate integers from real numbers because we often use the discrete nature of integers, e.g. as labels.
Since continuous functions can be passed to the higher-order pointwise function, it is easy to implement a continuous image look-up table. For example, a continuous version of an inverse look-up table can be defined as follows:
inverseLookUpTable2 range = unaryPointwise ((+range).neg)
Needless to say, more complicated look-up is possible, such as a table obtained by histogram equalisation.
3.3.4 Spatial conditional operations
The next example is a spatial conditional operation, or parallel-if, which is often used for parallel
computers and does selection using boolean array objects [Hockney81a]. As illustrated in Figure 3-3, a spatial conditional operation returns pixels from either the second or third image depending on the boolean pixels in the first image.
- 4 4- iml im2 im3 Spatial conditional 23 5 37 10 12 64 8 15 22 20 54 0 49 31 6 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 5 37 10 12 0 0 15 22 0 0 0 49 31 6 25
Figure 3-3 A spatial conditional o p eratio n
It can be defined in Miranda as:
condlmage iml im2 im3
* binaryPointwise cond iml (binaryPointwise pair im2 im3) where cond True (a,b) =» a
cond False (a,b) = b pair a b = (a,b)
This function takes three images: the first image is a boolean image and the second and third images are polymorphic images which have the same type. Depending on the contents of the boolean image, the function condlm age returns pixels either in the second or the third image. Because the type of a pixel is defined as a polymorphic type, operations on mixed image types like this can be implemented without difficulty. Please also note that only the overlapping part of the three operand images is returned.