Return to ING home page5.1 Classes for storing pixels

This page is part of the ING document INS-DAS-31 Design notes for UltraDAS

The preferred hierarchy

Figure 5.1.1 shows a desireable class-hierarchy for storing the pixels collected during one run on a camera.


Figure 5.1.1: preferred class-hiearchy for storing pixels.

This says that a pixels live in Raster objects, which are aggregated into Region objects along with Space and Extent objects. The Extent object describes the volume of space occupied by the Raster, and the Space describes the co-ordinate system used inside the Extent. The composite Region knows how to do geometry, and knows, and knows where its pixels fit in d-space. As a special case, a Region may be made without a Raster, and it then serves as a kind of geometric template. The Raster is the basic unit for for storing a contiguous array of pixels, arranged in a 1D, 2D or 3D block. The pixels of the Raster form a contiguous series along each of its axes. The Region is the basic unit for handling an array of pixels, since it knows about geometry and co-ordinate systems via its Space object. Clients of the Region would not be expected to address its Raster, Space or Extent directly.

Udas_camera tends to produce sets of associated regions, the pixels of which are not contiguous in any Space. These are modelled by the Mosaic, which is an un-ordered collection of Regions.

Readout is an important specialization of Mosaic: it is a Mosaic in which all the Regions were generated by the same readout, or by the same processing of a set of readouts. (The latter case applies where readouts are coadded: you put several Readouts in and get one out.) Because of its special properties, the Readout knows about the timing of the associated integration and exposure, and it includes timing attributes.

In some observations, notably multiple non-destructive readouts of IR cameras, many Readouts are collected under one run-number. This usage is modelled in the Pile class, which is an ordered set of Readouts. A Pile can represent all the Readouts of a run, preserving order of acquisition, or it can represent a disjoint image-cube, as acquired when observing with Taurus.

Once the Pile concept is adopted, a Run object is logically a specialization of Pile that knows how to acquire process and dispense the Readouts in the Pile. The Run class adds readout and archive methods to the passive Pile.

This is the idealized hierarchy. The actual software is rather different...
 

The actual hierarchy

Figure 5.1.2 shows the class hierarchy that was actually coded for UltraDAS. This has diverged from the ideal quite dramatically, due both to necessity and historical accident.


Figure 5.1.2: actual class-hierarchy in which UltraDAS stores pixels.

Firstly, note that there is no inheritance. Inheritance, which would be trivially easy in C++, is simply too hard to code in C, so composition is used instead. As coded, a Run is not a Pile, but it has a Pile; a Readout is not a Mosaic but it has a Mosaic. This means that operations on Pile are not automatically available on Run, nor are operations of Mosaic automatically available for Readout. Instead, one has to code delegating methods in Run and Readout. This is a pity.

Secondly, the Extent and Raster classes have disappeared and are now non-object attributes of Region; this is partly due to historical accident and partly to pragmatism. Originally, Region included all the intended function of Extent, Raster and Space, and indeed this is a perfectly reasonable structure; Rasters need Extents and Spaces to work, and Extents are meaningless without Spaces. Later, the Space class was recognized as a reusable artifact and was separated from Region; and indeed, Spaces are meaningful independently of Regions. The result is ugly in its asymmety. It isn't feasible to roll Space back into Region, as Spaces are used separately (see the Formatter code, for example). It would be possible to extract Raster and Extent as separate classes, but this would involve a lot of extra work for little gain, since establishing new classes in C is labout-intensive; it would reduce the overall bulk of Region, but this may not be reason enough. In the end, the existing arrangement seems like the best compromise.

The collection objects Pile and Mosaic include iterators that dispense references to the Readouts and Regions respectively. That is, its is legitimate for a client class to gain access to the aggregated objects and to process them directly. However, the client class is not supposed to take ownership of the objects inside a Pile or Mosaic; it cannot destroy them.

The Region object includes one method that returns a pointer to the first pixel in its Raster. This seems to be necessary for efficient access, especially when pixels are being dealt into the raster. However, this direct access must be used very carefully; the pointer to the raster is only valid between method calls on the object.

There is a method Mosaic::process_mosaic that causes the Regions, or "tiles" of the Mosaic to be combined according to a set of mapping previously set up. (Typically, the Formatter sets the mappings.) This method can take some time, and the class provides a progress-inspector method that can be called from a different thread in order to send periodic progress reports to the Facade. Because of this, Mosaic is MT-safe. The other pixel-storing objects are not MT-safe and the client code must take care not to call them for more than thread at once. In practice, this is not a problem, as the Pixel-storing methods are usually owned and used by only one parent object. The Pile class, which also does lengthy processing, should probably be reworked to allow polling for progress, in the same way as for Mosaic.

The pixel-storing classes have been arranged to improve encapsulation without requiring too much extra code. For example, the Pile::process_pile method does the image-processing that the Run deems necessary without exploring the Pile via its iterator. However, the image-combining methods in the Readout class operate directly on the Regions of the Readout's Mosaic because this seems to allow neater and simpler code.