Examples of VPA tables

<< Click to Display Table of Contents >>

Navigation:  Image Manager > CVB Technology > Common Image Model >

Examples of VPA tables

 

To refute the possible objection that the tables, although convenient and fast to use, are somewhat complicated and difficult to create we will present a small number of practical examples.

 

First we state the equations defining the tables for a generic linear memory representation, then specialise with some frequent cases and also present examples outside the scope of linear representations.

 

Generic linear memory representation, M(X, Y) = M(0, 0) + X*DeltaX + Y*DeltaY

VPAT   [X].XEntry := M(0, 0) + X*DeltaX

VPAT   [Y].YEntry := Y*DeltaY.

The equations for the tables are always understood for X = 0, ..., Width - 1 and Y = 0, ..., Height - 1.

 

Note that the tables can be programmed sequentially so that multiplications do not have to be carried out. It suffices to add DeltaX or DeltaY when incrementing the table index.
The same simplification applies to most other equations below, however even if multiplication were carried out it the overhead would not be high :
The tables have order o(Width + Height) as opposed to the image itself which has order o(Width * Height).
It is also important to remember that they need to be programmed just once for an image object.

 

Note that the way the VPA tables are used, we could have included offset M(0, 0) just as well in the Ytable as in the Xtable.

 

Example 1

 

For a practical example first consider an 8-bit gray scale image with width W and height H, the gray values are sequentially written in memory, with the address incremented from the last pixel of one scan line to the first pixel of the next scan line.
A simple 4-byte pointer lpImage points to the first pixel in the first scan line (this is the simplest image format):

IImage.Dimension              = 1

IImage.Width                  = W

IImage.Height                 = H

IImage.Datatype               = 8

(We omit the index for one-dimensional images)

IImageVPA.BaseAddress    = lpImage

IImageVPA.VPAT  [X].XEntry    = X

IImageVPA.VPAT  [Y].YEntry    = Y * W

 

Example 2

 

Next in complexity we consider an 8-bit DIB in the upside down format where the last scan line comes first in memory. There is an added complexity because, for DIBs, the scan lines wrap on Dword boundaries. In this case we have the pixel data represented by a memory handle hImage which needs to be locked in memory prior to addressing:

IImage.Dimension               = 1

IImage.Width                   = W

IImage.Height                  = H

IImage.Datatype                = 8

IImageVPA.BaseAddressIndex)    = lpImage

IImageVPA.VPAT  [X].XEntry     = X

IImageVPA.VPAT  [Y].YEntry     = ((W + 3) and 0xFFFFFFFC)*(H - Y - 1)

The complicated »(W + 3) and 0xFFFFFFFC)« simply gives the length of a scan line in memory extended to Dword boundaries.
The factor (H - Y - 1) implements the upside down order of scan lines: For Y = 0 this is (H - 1) i.e. the last scan line in memory and the first in the logical image.
For Y = H - 1 (last row in image) it becomes zero, addressing the first scan line in memory.

 

Example 3

 

Here is the image definition for a 24-bit RGB DIB, again upside down, referenced with a simple pointer lpImage to the bitmap data.
Recall that the values in memory are now BGRBGRBGR..., however we want plane 0 to refer to the red plane (otherwise it would be a BGR image) :

IImage.Dimension              = 3

IImage.Width                  = W

IImage.Height                 = H

IImage.Datatype               = 8

(Don't let this confuse you: the 24 bits refer to RGB, individual color planes are still 8-bit images)

 

IImageVPA.BaseAddress           = lpImage

IImageVPA.VPAT[X].XEntry        = 3*X

IImageVPA.VPAT(0)[Y].YEntry     = ((3*W + 3)and 0xFFFFFFFC)*(H - Y - 1) + 2

(The added »2« takes care of the red plane)

IImageVPA.VPAT(1)[Y].YEntry     = ((3*W + 3)and 0xFFFFFFFC)*(H - Y - 1) + 1

(Green plane)

IImageVPA.VPAT(2)[Y].YEntry     = ((3*W + 3)and 0xFFFFFFFC)*(H - Y - 1) + 0

(Blue plane) Note that the Xtable does not depend on the color.

Example 4

Now you may want to regard the RGB image as a single entity rather than as a stack of three gray images. In this case you can change the definition to the following (listing only the functions which change):

IImage.Dimension             = 1

IImage.Datatype              = 24

(This provides the desired 24-bit BGR pixels)

IImageVPA.VPAT[X].XEntry     = 3*X

IImageVPA.VPAT[Y].YEntry     = ((3*W + 3) and 0xFFFFFFFC) * (H - Y - 1)

(Addressing the BGR scan lines)

 

Example 5

 

A single, elegant formulation combines both cases by creating a four-dimensional image where planes 0, 1 and 2 give the R, G and B images, respectively, and plane 3 the combined BGR pixels:

IImage.Dimension              = 4

IImage.Width                  = W

IImage.Height                 = H

IImage.Datatype(Index)        = 8 for Index = 0,1,2 and

IImage.Datatype  (3)          = 24

(Here the fourth plane actually has a different data type)

IImageVPA.VPAT[X].XEntry      = 3*X

IImageVPA.VPAT(0)[Y].YEntry   = ((3*W + 3) and 0xFFFFFFFC) * (H - Y - 1) + 2

(The added »2« takes care of the red plane)

IImageVPA.VPAT(1)[Y].YEntry   = ((3*W + 3) and 0xFFFFFFFC) * (H - Y - 1) + 1

(Green plane)

IImageVPA.VPAT(2)[Y].YEntry   = ((3*W + 3) and 0xFFFFFFFC) * (H - Y - 1) + 0

(Blue plane)

IImageVPA.VPAT(3)             = IImageVPA.VPAT   (2)

Addressing for the fourth plane is the same as for the blue plane. Only interpretation of the address content is different.

 

Example 6

 

Next we consider a complex, double-precision image such as the output of some FFT functions. One memory block (lpReal) contains the real values, the other (lpImaginary) the imaginary ones in a simple scan line format (as the first example). We want to interpret plane 0 as the real part and plane 1 as the imaginary part:

IImage.Dimension               = 2

IImage.IWidth                  = W

IImage.IHeight                 = H

IImage.Datatype                = 64 + float + signed

(Each plane has double-precision pixel values)

IImageVPA.BaseAddress  (0)     = lpReal

IImageVPA.BaseAddress  (1)     = lpImaginary

IImageVPA.VPAT  [X].XEntry     = X*8

IImageVPA.VPAT  [Y].YEntry     = Y*W*8

Note that in this case a pointer to one VPA table can be used for both image planes.

 

Example 7

 

In the next example a frame grabber (512 x 512 x 8) has nonsquare pixels with an aspect ratio of 3:2 and deposits the scan lines in interlaced format, the even field in the first 128 K of the memory block and the odd field in the second one. Part of the object is to square the pixels by mapping linearly onto a 768 x 512 rectangle (without antialiasing).

IDimension                  = 1

IWidth                      = 768

IHeight                     = 512

IDMType         (Index)     = 0

IDMBaseAddress  (Index)     = lpImage

VPAT  [X].XEntry            = (2*X) div 3

This performs the squaring of pixels

VPAT  [Y].YEntry            = 512*(Y div 2) for even Y and

VPAT  [Y].YEntry            = 131072 + 512*(Y div 2) for odd Y

Subsequent to programming of the tables, the complexities of the interlaced case are hidden to any processing software.

 

Also this is a situation where the geometry of an image is actually modified using the tables - virtually squaring the pixels.
Of course the resulting image still has a fault resulting from aliasing (in every row every other pixel is duplicated).
Nevertheless, if an algorithm is not too sensitive to high local frequencies it can be ported to this type of image without modification.

 

Note that in all of the above examples, the image pixels are accessed in a unified way once the tables have been programmed.
If we forget about the complex floating point and BGR images for the moment, we can actually state that any processing routine which works on one image plane of the above images will also work on all the other image planes of all the other images above.

 

Example 8

 

In the next example we suppose an image A as already been defined in the VPA syntax above, and describe the definition of another image B, of prescribed width WB and height HB, mapping a subrectangle [Left, Top, Right, Bottom] of image A onto [0, 0, WB - 1, HB - 1].

IDimensionB            = IDimensionA

IWidthB                = WB

IheightB               = HB

IDMTypeB  (Index)      = IDMTypeA (Index)

IDMBaseAddress  (Index)= IDMBaseAddressA (Index)

This implies that the memory for image A must not be freed while image B is in use. See reference counting below.

VPATB  [X].XEntry := VPATA [Left + (X * (Right - Left)) /(WB - 1)].XEntry

VPATB  [Y].YEntry := VPATA [Top + (Y * (Bottom - Top)) /(HB - 1)].YEntry

The tables implement the affine map required (this method of scaling again does not provide any antialiasing). In a similar way, images can be virtually rotated by 90° or reflected - always with a number of computations in the order of o(Width + Height) rather than o(Width * Height).

 

Finally we describe a problem which occurs when filtering an image with a convolution kernel, and its solution using VPA. In this case the output is first defined for possible translation positions of the kernel within the input image. Assuming a 5 x 5 kernel and a 512 x 512 input image, the output image would be 508 x 508. This is implemented in the present version of MINOS, resulting in harsh criticism by users who wish the output image to have the same dimensions regardless of the arbitrary definition of the boundary pixels.

 

The additional pixels cannot be left completely uninitialized. It has been proposed to copy the values of adjacent, well defined pixels to the fringe pixels after the filtering. Our suggestion is to extend the input image at the fringes before filtering to create a 516 x 516 image which is then filtered in a straightforward way.

 

Extending the image is very simple with the tables. Suppose we want to extend the image by DFringe on each of the four sides. This is done by

VPAT  [X].XEntry := VPAT [0].XEntry - DFringe <= X <0 ,

VPAT  [X].XEntry := VPAT [Width - 1] .XEntry,

for Width - 1<X<= Width + DFringe - 1,

VPAT  [Y].YEntry := VPAT [0].YEntry,  for -DFringe <= Y <0 ,

VPAT  [Y].YEntry := VPAT [Height - 1].YEntry,

for Height - 1<Y<= Height + DFringe - 1.

This requires an overhead of 4*DFringe operations when the tables are programmed and saves 2*DFringe*Width + 2*DFringe*Height operations when the image is filtered.
The additional memory requirement is 16*Dboundary bytes.
If we regard, say 9 x 9, as the largest kernel size we wish to implement without any special processing, this additional memory requirement is 64 bytes which is so small that we propose to make such an extension the standard for images.