# Documentation

## Performing Distinct Block Operations

### Understanding Distinct Block Processing

In distinct block processing, you divide a matrix into `m`-by-`n` sections. These sections, or distinct blocks, overlay the image matrix starting in the upper left corner, with no overlap. If the blocks do not fit exactly over the image, you can add padding to the image or work with partial blocks on the right or bottom edges of the image. The following figure shows a 15-by-30 matrix divided into 4-by-8 blocks. The right and bottom edges have partial blocks. You can process partial blocks as is, or you can pad the image so that the resulting size is 16-by-32. For more information, see Applying Padding. (To operate on an image a pixel at a time, rather than a block at a time, use the sliding neighborhood processing function. For more information, see Performing Sliding Neighborhood Operations.)

Image Divided into Distinct Blocks

### Implementing Block Processing Using the blockproc Function

To perform distinct block operations, use the `blockproc` function. The `blockproc` function extracts each distinct block from an image and passes it to a function you specify for processing. The `blockproc` function assembles the returned blocks to create an output image.

For example, the commands below process image `I` in 25-by-25 blocks with the function `myfun`. In this case, the `myfun` function resizes the blocks to make a thumbnail. (For more information about using function handles and anonymous functions, see `function_handle` in the MATLAB® Function Reference documentation.)

```myfun = @(block_struct) imresize(block_struct.data,0.15); I = imread('tire.tif'); I2 = blockproc(I,[25 25],myfun); ```
 Note:   Due to block edge effects, resizing an image using `blockproc` does not produce the same results as resizing the entire image at once.

The example below uses the `blockproc` function to set every pixel in each 32-by-32 block of an image to the average of the elements in that block. The anonymous function computes the mean of the block, and then multiplies the result by a matrix of ones, so that the output block is the same size as the input block. As a result, the output image is the same size as the input image. The `blockproc` function does not require that the images be the same size. If this is the result you want, make sure that the function you specify returns blocks of the appropriate size:

```myfun = @(block_struct) ... uint8(mean2(block_struct.data)*... ones(size(block_struct.data))); I2 = blockproc('moon.tif',[32 32],myfun); figure; imshow('moon.tif'); figure; imshow(I2,[]);```

 Note:   Many operations that `blockproc` can implement run much faster if the computations are performed on matrix columns rather than rectangular blocks. For information about this approach, see Using Columnwise Processing to Speed Up Sliding Neighborhood or Distinct Block Operations.

When processing an image in blocks, you may wish to add padding for two reasons:

• To address the issue of partial blocks

• To create overlapping borders

As described in Understanding Distinct Block Processing, if blocks do not fit exactly over an image, partial blocks occur along the bottom and right edges of the image. By default, these partial blocks are processed as is, with no additional padding. Set the `'PadPartialBlocks'` parameter to `true` to pad the right or bottom edges of the image and make the blocks full-sized.

You can also add borders to each block. Use the `'BorderSize'` parameter to specify extra rows and columns of pixels outside the block whose values are taken into account when processing the block. When there is a border, `blockproc` passes the expanded block, including the border, to the specified function.

Image Divided into Distinct Blocks with Specified Borders

To process the blocks in the figure above with the function handle `myfun`, the call is:

```B = blockproc(A,[4 8],myfun,'BorderSize',[1 2], ... 'PadPartialBlocks',true) ```

Both padding of partial blocks and block borders add to the overall size of the image, as you can see in the figure. The original 15-by-30 matrix becomes a 16-by-32 matrix due to padding of partial blocks. Also, each block in the image is processed with a 1-by-2 pixel border—one additional pixel on the top and bottom edges and two pixels along the left and right edges. Blocks along the image edges, expanded to include the border, extend beyond the bounds of the original image. The border pixels along the image edges increase the final size of the input matrix to 18-by-36. The outermost rectangle in the figure delineates the new boundaries of the image after all padding has been added.

By default, `blockproc` pads the image with zeros. If you need a different type of padding, use the `blockproc` function's `'PadMethod'` parameter.

### Block Size and Performance

When using the `blockproc` function to either read or write image files, the number of times the file is accessed can significantly affect performance. In general, selecting larger block sizes reduces the number of times `blockproc` has to access the disk, at the cost of using more memory to process each block. Knowing the file format layout on disk can help you select block sizes that minimize the number of times the disk is accessed. See the `blockproc` reference page for more information about file formats.

#### TIFF Image Characteristics

TIFF images organize their data on disk in one of two ways: in tiles or in strips. A tiled TIFF image stores rectangular blocks of data contiguously in the file. Each tile is read and written as a single unit. TIFF images with strip layout have data stored in strips; each strip spans the entire width of the image and is one or more rows in height. Like a tile, each strip is stored, read, and written as a single unit.

When selecting an appropriate block size for TIFF image processing, understanding the organization of your TIFF image is important. To find out whether your image is organized in tiles or strips, use the `imfinfo` function.

The struct returned by `imfinfo` for TIFF images contains the fields `TileWidth` and `TileLength`. If these fields have valid (nonempty) values, then the image is a tiled TIFF, and these fields define the size of each tile. If these fields contain values of empty (`[]`), then the TIFF is organized in strips. For TIFFs with strip layout, refer to the struct field `RowsPerStrip`, which defines the size of each strip of data.

When reading TIFF images, the minimum amount of data that can be read is a single tile or a single strip, depending on the type of TIFF. To optimize the performance of `blockproc`, select block sizes that correspond closely with how your TIFF image is organized on disk. In this way, you can avoid rereading the same pixels multiple times.

#### Choosing Block Size

The following three cases demonstrate the influence of block size on the performance of `blockproc`. In each of these cases, the total number of pixels in each block is approximately the same; only the size of the blocks is different.

First, read in an image file and convert it to a TIFF.

```imageA = imread('concordorthophoto.png','PNG'); imwrite(imageA,'concordorthophoto.tif','TIFF');```

Use `imfinfo` to determine whether `concordorthophoto.tif` is organized in strips or tiles.

```imfinfo concordorthophoto.tif ```

Select fields from the struct appear below:

``` Filename: 'concordorthophoto.tif' FileSize: 6595038 Format: 'tif' Width: 2956 Height: 2215 BitDepth: 8 ColorType: 'grayscale' BitsPerSample: 8 StripOffsets: [1108x1 double] SamplesPerPixel: 1 RowsPerStrip: 2 PlanarConfiguration: 'Chunky' TileWidth: [] TileLength: []```

The value 2 in `RowsPerStrip` indicates that this TIFF image is organized in strips with two rows per strip. Each strip spans the width of the image (2956 pixels) and is two pixels tall. The following three cases illustrate how choosing an appropriate block size can improve performance.

Case 1: Typical Case — Square Block.  First try a square block of size `[500 500]`. Each time the `blockproc` function accesses the disk it reads in an entire strip and discards any part of the strip not included in the current block. With two rows per strip and 500 rows per block, the `blockproc` function accesses the disk 250 times for each block. The image is 2956 pixels wide and 500 rows wide, or approximately six blocks wide (2956/500 = 5.912). The `blockproc` function reads the same strip over and over again for each block that includes pixels contained in that strip. Since the image is six blocks wide, `blockproc` reads every strip of the file six times.

```tic, im = blockproc('concordorthophoto.tif',[500 500],@(s) s.data); toc ```
`Elapsed time is 17.806605 seconds.`

Case 2: Worst Case — Column-Shaped Block.  The file layout on the disk is in rows. (Stripped TIFF images are always organized in rows, never in columns.) Try choosing blocks shaped like columns of size [2215 111]. Now the block is shaped exactly opposite the actual file layout on disk.

The image is over 26 blocks wide (2956/111 = 26.631). Every strip must be read for every block. The `blockproc` function reads the entire image from disk 26 times. The amount of time it takes to process the image with the column-shaped blocks is proportional to the number of disk reads. With about four times as many disk reads in Case 2, as compared to Case 1, the elapsed time is about four times as long.

```tic, im = blockproc('concordorthophoto.tif',[2215 111],@(s) s.data); toc ```
`Elapsed time is 60.766139 seconds.`

Case 3: Best Case — Row-Shaped Block.  Finally, choose a block that aligns with the TIFF strips, a block of size [84 2956]. Each block spans the width of the image. Each strip is read exactly one time, and all data for a particular block is stored contiguously on disk.

```tic, im = blockproc('concordorthophoto.tif',[84 2956],@(s) s.data); toc ```
`Elapsed time is 4.610911 seconds.`

### Using Parallel Block Processing on large Image Files

If you have a Parallel Computing Toolbox™ license, you can take advantage of multiple processor cores on your machine by specifying the `blockproc` setting `'UseParallel'` as `true`. Doing so divides the block processing among all available MATLAB sessions to potentially improve the performance of `blockproc`.

#### What is Parallel Block Processing?

Parallel block processing allows you to process many blocks simultaneously by distributing task computations to a collection of MATLAB sessions, called workers. The MATLAB session with which you interact is called the client. The client reserves a collection of workers, called a MATLAB pool. Then the client MATLAB session divides the input image and sends sections to the worker MATLAB sessions. Each worker processes a subset of blocks and sends the results back to the client. The client MATLAB collects the results into an output variable.

When you set `'UseParallel'` to `true`, `blockproc` uses all available workers in the MATLAB pool to process the input image in parallel.

To read more about parallel computing, see Key Problems Addressed by Parallel Computing and Introduction to Parallel Solutions in the Parallel Computing Toolbox User's Guide.

#### When to Use Parallel Block Processing

When processing small images, serial mode is expected to be faster than parallel mode. For larger images, however, you may see significant performance gains from parallel processing. The performance of parallel block processing depends on three factors:

• Function used for processing

• Image size

• Block size

In general, using larger blocks while block processing an image results in faster performance than completing the same task using smaller blocks. However, sometimes the task or algorithm you are applying to your image requires a certain block size, and you must use smaller blocks. When block processing using smaller blocks, parallel block processing is typically faster than regular (serial) block processing, often by a large margin. If you are using larger blocks, however, you might need to experiment to determine whether parallel block processing saves computing time.

#### How to Use Parallel Block Processing

You must meet two conditions to use parallel block processing:

• The source image is not specified as an `ImageAdapter` class.

• A Parallel Computing Toolbox license exists in the MATLAB installation.

If you meet these conditions, you can enable parallel block processing by opening a MATLAB pool:

`parpool(4)`

Here, 4 represents the number of workers in your pool. (See the `parpool` reference page for more details.)

After opening a MATLAB pool, you can invoke parallel processing in `blockproc` by specifying `'UseParallel'` as `true`. In the following example, compute a discrete cosine transform for each 8 x 8 block of an image in parallel:

```blockFun = @(block_struct) dct2(block_struct.data); result = blockproc(input_image,[8 8], blockFun, ... 'UseParallel',true); ```

### Working with Data in Unsupported Formats

In addition to reading TIFF or JPEG2000 files and writing TIFF files, the `blockproc` function can read and write other formats. To work with image data in another file format, you must construct a class that inherits from the `ImageAdapter` class. The `ImageAdapter` class is an abstract class that is part of the Image Processing Toolbox™ software. It defines the signature for methods that `blockproc` uses for file I/O with images on disk. You can associate instances of an Image Adapter class with a file and use them as arguments to `blockproc` for file-based block processing.

This section demonstrates the process of writing an Image Adapter class by discussing an example class (the `LanAdapter` class). The `LanAdapter` class is part of the toolbox. Use this simple, read-only class to process arbitrarily large `uint8` LAN files with `blockproc`.

#### Learning More About the LAN File Format

To understand how the `LanAdapter` class works, you must first know about the LAN file format. Landsat thematic mapper imagery is stored in the Erdas LAN file format. Erdas LAN files contain a 128-byte header followed by one or more spectral bands of data, band-interleaved-by-line (BIL), in order of increasing band number. The data is stored in little-endian byte order. The header contains several pieces of important information about the file, including size, data type, and number of bands of imagery contained in the file. The LAN file format specification defines the first 24 bytes of the file header as shown in the table.

BytesData TypeContent
1–66-byte character string `'HEADER'` or `'HEAD74'`
7–816-bit integerPack type of the file (indicating bit depth)
9–1016-bit integerNumber of bands of data
11–166 bytesUnused
17–2032-bit integerNumber of columns of data
21–2432-bit integerNumber of rows of data

The remaining 104 bytes contain various other properties of the file, which this example does not use.

Typically, when working with LAN files, the first step is to learn more about the file by parsing the header. The following code shows how to parse the header of the `rio.lan` file:

1. Open the file:

```file_name = 'rio.lan'; fid = fopen(file_name,'r'); ```

```header_str = fread(fid,6,'uint8=>char')'; fprintf('Header String: %s\n',header_str); ```

```pack_type = fread(fid,1,'uint16',0,'ieee-le'); fprintf('Pack Type: %d\n',pack_type);```
4. Read the number of spectral bands:

```num_bands = fread(fid,1,'uint16',0,'ieee-le'); fprintf('Number of Bands: %d\n',num_bands);```
5. Read the image width and height:

```unused_bytes = fread(fid,6,'uint8',0,'ieee-le'); width = fread(fid,1,'uint32',0,'ieee-le'); height = fread(fid,1,'uint32',0,'ieee-le'); fprintf('Image Size (w x h): %d x %d\n',width,height); ```
6. Close the file:

`fclose(fid);`

The output appears as follows:

```Header String: HEAD74 Pack Type: 0 Number of Bands: 7 Image Size (w x h): 512 x 512 ```

The `rio.lan` file is a 512 x 512, 7-band image. The pack type of 0 indicates that each sample is an 8-bit, unsigned integer (`uint8` data type).

In a typical, in-memory workflow, you would read this LAN file with the `multibandread` function. The LAN format stores the RGB data from the visible spectrum in bands 3, 2, and 1, respectively. You could create a truecolor image for further processing.

```truecolor = multibandread('rio.lan', [512, 512, 7],... 'uint8=>uint8', 128,'bil', 'ieee-le', {'Band','Direct',[3 2 1]}); ```

For very large LAN files, however, reading and processing the entire image in memory using `multibandread` can be impractical, depending on your system capabilities. To avoid memory limitations, use the `blockproc` function. With `blockproc`, you can process images with a file-based workflow. You can read, process, and then write the results, one block at a time.

The `blockproc` function only supports reading and writing certain file formats, but it is extensible via the `ImageAdapter` class. To write an Image Adapter class for a particular file format, you must be able to:

• Query the size of the file on disk

• Read a rectangular block of data from the file

If you meet these two conditions, you can write an Image Adapter class for LAN files. You can parse the image header to query the file size, and you can modify the call to `multibandread` to read a particular block of data. You can encapsulate the code for these two objectives in an Image Adapter class structure, and then operate directly on large LAN files with the `blockproc` function. The `LanAdapter` class is an Image Adapter class for LAN files, and is part of the Image Processing Toolbox software.

This section describes the constructor, properties, and methods of the `LanAdapter` class. Studying the `LanAdapter` class helps prepare you for writing your own Image Adapter class. If you are new to object-oriented programming, see Developing Classes—Typical Workflow for general information on writing classes in the MATLAB documentation.

Open `LanAdapter.m` and look at the implementation of the `LanAdapter` class.

Classdef.  The `LanAdapter` class begins with the keyword `classdef`. The `classdef` section defines the class name and indicates that `LanAdapter` inherits from the `ImageAdapter` superclass. Inheriting from `ImageAdapter` allows the new class to:

• Interact with `blockproc`

• Define common `ImageAdapter` properties

• Define the interface that `blockproc` uses to read and write to LAN files

Properties.  Following the `classdef` section, the `LanAdapter` class contains two blocks of class properties. The first block contains properties that are publicly visible, but not publicly modifiable. The second block contains fully public properties. The `LanAdapter` class stores some information from the file header as class properties. Other classes that also inherit from `ImageAdapter`, but that support different file formats, can have different properties.

```classdef LanAdapter < ImageAdapter properties(GetAccess = public, SetAccess = private) Filename NumBands end properties(Access = public) SelectedBands end ```

In addition to the properties defined in `LanAdapter.m`, the class inherits the `ImageSize` property from the `ImageAdapter` superclass. The new class sets the `ImageSize` property in the constructor.

Methods: Class Constructor.  The class constructor initializes the `LanAdapter` object. The `LanAdapter` constructor parses the LAN file header information and sets the class properties. Implement the constructor, a class method, inside a `methods` block.

The constructor contains much of the same code used to parse the LAN file header. The `LanAdapter` class only supports `uint8` data type files, so the constructor validates the pack type of the LAN file, as well as the header string. The class properties store the remaining information. The method responsible for reading pixel data uses these properties. The `SelectedBands` property allows you to read a subset of the bands, with the default set to read all bands.

``` methods function obj = LanAdapter(fname) % LanAdapter constructor for LanAdapter class. % When creating a new LanAdapter object, read the file % header to validate the file as well as save some image % properties for later use. % Open the file. obj.Filename = fname; fid = fopen(fname,'r'); % Verify that the file begins with the string 'HEADER' or % 'HEAD74', as per the Erdas LAN file specification. header_str = fread(fid,6,'uint8=>char'); if ~(strcmp(header_str','HEADER') || strcmp(header_str',... 'HEAD74')) error('Invalid LAN file header.'); end % Read the data type from the header. pack_type = fread(fid,1,'uint16',0,'ieee-le'); if ~isequal(pack_type,0) error('Unsupported pack type. The LanAdapter example only... supports reading uint8 data.'); end % Provide band information. obj.NumBands = fread(fid,1,'uint16',0,'ieee-le'); % By default, return all bands of data obj.SelectedBands = 1:obj.NumBands; % Specify image width and height. unused_field = fread(fid,6,'uint8',0,'ieee-le'); %#ok<NASGU> width = fread(fid,1,'uint32',0,'ieee-le'); height = fread(fid,1,'uint32',0,'ieee-le'); obj.ImageSize = [height width]; % Close the file handle fclose(fid); end % LanAdapter ```

Methods: Required.  Adapter classes have two required methods defined in the abstract superclass, `ImageAdapter`. All Image Adapter classes must implement these methods. The `blockproc` function uses the first method, `readRegion`, to read blocks of data from files on disk. The second method, `close`, performs any necessary cleanup of the Image Adapter object.

``` function data = readRegion(obj, region_start, region_size) % readRegion reads a rectangular block of data from the file. % Prepare various arguments to MULTIBANDREAD. header_size = 128; rows = region_start(1):(region_start(1) + region_size(1) - 1); cols = region_start(2):(region_start(2) + region_size(2) - 1); % Call MULTIBANDREAD to get data. full_size = [obj.ImageSize obj.NumBands]; data = multibandread(obj.Filename, full_size,... 'uint8=>uint8', header_size, 'bil', 'ieee-le',... {'Row', 'Direct', rows},... {'Column','Direct', cols},... {'Band', 'Direct', obj.SelectedBands}); end % readRegion ```

`readRegion` has two input arguments, `region_start` and `region_size`. The `region_start` argument, a two-element vector in the form `[row col]`, defines the first pixel in the request block of data. The `region_size` argument, a two-element vector in the form `[num_rows num_cols]`, defines the size of the requested block of data. The `readRegion` method uses these input arguments to read and return the requested block of data from the image.

The `readRegion` method is implemented differently for different file formats, depending on what tools are available for reading the specific files. The `readRegion` method for the `LanAdapter` class uses the input arguments to prepare custom input for `multibandread`. For LAN files, `multibandread` provides a convenient way to read specific subsections of an image.

The other required method is `close`. The `close` method of the `LanAdapter` class appears as follows:

``` function close(obj) %#ok<MANU> % Close the LanAdapter object. This method is a part % of the ImageAdapter interface and is required. % Since the readRegion method is "atomic", there are % no open file handles to close, so this method is empty. end end % public methods end % LanAdapter ```

As the comments indicate, the `close` method for `LanAdapter` has nothing to do, so `close` is empty. The `multibandread` function does not require maintenance of open file handles, so the `close` method has no handles to clean up. Image Adapter classes for other file formats may have more substantial `close` methods including closing file handles and performing other class clean-up responsibilities.

Methods (Optional).  As written, the `LanAdapter` class can only read LAN files, not write them. If you want to write output to a LAN format file, or another file with a format that `blockproc` does not support, implement the optional `writeRegion` method. Then, you can specify your class as a `'Destination'` parameter in `blockproc` and write output to a file of your chosen format.

The signature of the `writeRegion` method is as follows:

`function [] = writeRegion(obj, region_start, region_data)`

The first argument, `region_start`, indicates the first pixel of the block that the `writeRegion` method writes. The second argument, `region_data`, contains the new data that the method writes to the file.

Classes that implement the `writeRegion` method can be more complex than `LanAdapter`. When creating a writable Image Adapter object, classes often have the additional responsibility of creating new files in the class constructor. This file creation requires a more complex syntax in the constructor, where you potentially need to specify the size and data type of a new file you want to create. Constructors that create new files can also encounter other issues, such as operating system file permissions or potentially difficult file-creation code.

#### Using the LanAdapter Class with blockproc

Now that you understand how the `LanAdapter` class works, you can use it to enhance the visible bands of a LAN file. Run the Computing Statistics for Large Images (BlockProcessStatisticsExampleBlockProcessStatisticsExample) example to see how the `blockproc` function works with the `LanAdapter` class.