Tiles (Accessing more than a row)ΒΆ

A simple engine() which does a vertical blur one pixel high can be implemented with the following _request and _engine()

void _request(int x, int y, int r, int t, ChannelMask channels, int count)
{
  input(0)->request(x, y, r, t + 1, channels, 2);
}

void engine(int y, int l, int r, ChannelMask channels, Row& row)
{
  Row lower(l, r);
  Row upper(l, r);
  lower.get(input0(), y, l, r, channels);
  upper.get(input0(), y + 1, l, r, channels);
  foreach(z, channels) {
    for (int x = l; x < r; x++) {
      row.writable(z)[x] = (lower[z][x] + upper[z][x]) / 2.0f;
    }
  }
}

Firstly, request is implemented so that the region-of-interest of the Iop on its input is one pixel taller than the region of interest it has been passed. Secondly, the “count” parameter is put to two, to force caching of the input. This ensures that row 1 on the input is only generated once, and then re-used when it is fetched the second time, for example. This way, we can happily request data outside the bbox of the input, and even call get() on it, as it gets edge-extended.

There are problems with this method. With only two Row objects it is not so bad, but it is not a convenient form for randomly accessing or iterating over a larger area. This is what the Interest/Tile mechanism is for.

Tile can be used as follows

Tile tile(input0(), x, y, r, t, channels)

This forces the area (x, y, r, t) to be generated for channels. It will then exist on the Iops cache. By default Tile does not do any multithreading, although it is thread-safe, so that if multiple threads work on the same or overlapping Tiles they will happily cooperate until their particular region is done. If your engine has had to acquire a global lock (for example, it needs to analyse the entire input to produce some intermediate data, and after that, producing the output from the input is trivial), or indeed you are fetching image data from outside the context of a call to engine() (for example, in _validate or knob_changed, although it is noted that this is strongly advised against), then it may be that it is effectively being called from a single-threaded context. In that case another parameter of Tile can be used, to force the Tile to be filled in multi-threaded, launching new threads if necessary.

Tile tile(input0(), x, y, r, t, channels, true)

After the Tile has been created, it is possible to query it

An Interest is like a Tile, except that the Tile constructor blocks until all the rows in the area have actually been processed, and are available for immediate access. Interests will fetch data either through at() or load_range().

The pointers obtained from Tile/Interest are guaranteed to remain valid until the Tile/Interest destructor is called. After this, the cache is likely to be kept around, but may be purged in case of an out of memory condition. Once data is fetched, either through the constructor or load_range, you can get pointers to the data, with operator[].

operator[](Channel z) returns a pointer to a per-channel array of pointers to pointers to float. Like operator[](Channel z) on the row, the pointer is not immediately deferencable, and is only valid with a specified range of offsets (that is, the b and t values passed to the constructor of the Interest/Tile). So, this means that “tile[z][y][x]”. Unlike regular Row access, Tiling done this way does not do edge extension, so you must either implement that yourself, or use the at() function, which clamps the coordinates you pass it at the edge of the intersection of the tile and the bbox.

For example, here is a simple 3x3 box blur, implemented as with Tile

void _request(int x, int y, int r, int t, ChannelMask channels, int count)
{
  input(0)->request(x, y - 1, r, t + 1, channels);
}

void engine(int y, int l, int r, ChannelMask channels, Row& row)
{
  Tile t(input0(), l - 1, y - 1, r + 1, y + 2, channels);
  if (aborted())
    return;

  foreach(z, channels)
  {
    for (int x = l; x < r; x++) {

      float val = 0;

      for (int yoff = -1; yoff <= 1; yoff++) {
        for (int xoff = -1; xoff <= 1; xoff++) {
          val += t.at(xoff + x, yoff + y, z);
        }
      }

      row.writable(z)[x] = val / 9.0;
    }
  }
}