In contrast to point operators, which operate only locally, neighborhood operators use neighbors of pixels to determine output values. Examples of neighborhood operators are filters that remove noise, sharpen or blur edges, or perform locally adaptive thresholding or histogram equalization.

Median

The median filter is a non-linear noise reduction filter, which performs better than simple averaging in some cases, especially for salt-and-pepper noise.

For every pixel, the median filter selects the median value of all values in the neighborhood of the pixel.

// Median from a source view to a destination view.
template<typename VS, typename VD> 
void median(VS source, VD destination, ngi::vector<int> size);

// Median from a source view to a destination view, 
// inside a region only.
template<typename VS, typename VD> 
void median(VS source, region const& aoi, VD destination, 
    ngi::vector<int> size);

// Median from a source view to a destination view.
Filter.Median(View source, View dest, Vector size);

// Median from a source image to a destination image.
Image destination = source.Median(Vector size);

// Median from a source view to a destination view, 
// inside a region only.
Filter.Median(View source, Region aoi, View dest, Vector size);

// Median from a source image to a destination image, 
// inside a region only.
Image destination = source.Median(View source, Region aoi, 
    Vector size);

Here are some examples of the median filter:

Median with 1x3 kernel.

Median with 3x1 kernel.

Median with 3x3 kernel.

Median with 5x5 kernel.

Median with 7x7 kernel.

Median with 9x9 kernel.

Hybrid Median

The hybrid median filter is a non-linear noise reduction filter. It is more edge-preserving than the standard median filter. It calculates three medians in a 3x3 or 5x5 neighborhood: first the median of the horizontal and vertical neighbors is calculated (a cross-shaped kernel), then the median of the diagonal neighbors is calculated (an x-shaped kernel), and finally the median of these two intermediate results and the center pixel value itself is calculated and returned.

// Hybrid median from a source view to a destination view.
template<typename VS, typename VD> 
void hybrid_median(VS source, VD destination, int size);

// Hybrid median from a source view to a destination view, 
// inside a region only.
template<typename VS, typename VD> 
void hybrid_median(VS source, region const& aoi, VD destination, 
    int size);

// Hybrid median from a source view to a destination view.
Filter.HybridMedian(View source, View dest, int size);

// Hybrid median from a source image to a destination image.
Image destination = source.HybridMedian(int size);

// Hybrid median from a source view to a destination view, 
// inside a region only.
Filter.HybridMedian(View source, Region aoi, View dest, int size);

// Hybrid median from a source image to a destination image, 
// inside a region only.
Image destination = source.HybridMedian(Region aoi, int size);

Here are some examples of the hybrid median filter:

Hybrid median with 3x3 kernel.

Hybrid median with 5x5 kernel.

Binning

Analog binning is a concept used originally in CCD Cameras to reduce noise or increase sensitivity.

Neighboring pixels in the CCD chip are combined to blocks. Analog binning is implemented by adding the charges of the neighboring cells, which can be done both in the horizontal and in the vertical direction. This also increases the frame rate, because less pixels have to be digitized and transmitted.

Here is a sketch that shows 1x1 binning, which is a fancy way of meaning no binning at all.

CCD readout with no binning.

Accumulated charges in the pixels of the rows are shifted down line by line, and the bottom-most row is transferred into the Horizontal Register. For each row, the charges in the Horizontal Register are shifted right pixel by pixel into the Output and are then digitized.

The next sketch shows how 2x2 binning could be implemented in a CCD chip:

CCD readout with 2x2 binning.

Two rows are transferred into the Horizontal Register before the pixels are transferred right. The charge of two rows accumulates in the Horizontal Register. Then two pixels are transferred to the Output before digitization. Two charges of two pixels from the Horizontal Register accumulate in the Output. Effectively, accumulated charges of 2x2 pixels are digitized.

In principle, any combination of binning is possible, such as 2x1, 1x2, 3x3, nxm, etc. However, specific hardware chips are often limited and only offer some fixed subset of binning factors.

Analog binning can be mimicked by digital binning. Digital binning is done after digitization by adding the digital intensities of neighboring pixels.

Digital binning.

Adding is not the only way how the values of multiple pixels can be combined. In fact there are nearly endless possibilities of binning functions, and the NGI library implements quite a few of them. Also, the NGI library imposes no limitations on the binning factors: any size of rectangular x by y blocks is possible.

Arithmetic Mean

The arithmetic mean is similar to the charge accumulation performed by CCD chips in hardware. In order to avoid overflow, the accumulated value is normalized to yield the mean. The arithmetic mean is calculated according to the following formula:

$$am = \frac{1}{n} \sum_{k=1}^{n}i_k$$

More information about the arithmetic mean can be found in Wikipedia: http://en.wikipedia.org/wiki/Arithmetic_mean.

Here is a code example of how you would call apply arithmetic binning:

image<unsigned char> am = image<unsigned char>(img.width()/4, img.height()/4);
bin<arithmetic_mean_functor<unsigned char> >(img.get_view(), am.get_view());

The binning factor is calculated by the binning function from the sizes of the source and destination views.

Similar code can be used to bin color images:

image_type am = image_type(img.width()/4, img.height()/4);
bin<arithmetic_mean_functor<color_rgb<unsigned char>>(img.get_view(), am.get_view());

Note that the arithmetic_mean_functor has a second template parameter used for internal computation. If the binning factors become large, the internally calculated sum may overflow the result data type. A floating point type is therefore used for the internal calculations. In the monochrome example above this would be the type double, in the color example this would be the type color_rgb\<double>. The arithmetic_mean_functor as well as all other functors applicable in this context use the numeric_traits\<>::derive_type\<>::type type definition in order to deduce the correct intermediate type.

Geometric Mean

The geometric mean is calculated by multiplying values together and then taking the n-th root of the product.

$$gm = (\prod_{k=1}^{n} i_k)^{\frac{1}{n}}$$

More information about the geometric mean can be found in Wikipedia: http://en.wikipedia.org/wiki/Geometric_mean.

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by geometric_mean_functor.

Harmonic Mean

The harmonic mean is calculated by calculating the reciprocal of the arithmetic mean of reciprocals.

$$hm = \frac{n}{\sum_{k=1}^{n} i_k}$$

More information about the geometric mean can be found in Wikipedia: http://en.wikipedia.org/wiki/Harmonic_mean.

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by harmonic_mean_functor.

Quadratic Mean

The quadratic mean is calculated by calculating the root of the average of squares.

$$qm =\sqrt{\frac{\sum_{k=1}^{n}(i_k)^2)}{n}}$$

More information about the quadratic mean can be found in Wikipedia: http://en.wikipedia.org/wiki/Quadratic_mean.

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by quadratic_mean_functor.

Minimum

The minimum is calculated by taking the minimum of all values.

min = min{i1, i2, ..., in}

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by minimum_functor.

Maximum

The maximum is calculated by taking the maximum of all values.

max = max{i1, i2, ..., in}

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by maximum_functor.

Midrange

The midrange is calculated by taking the arithmetic average of the minimum and the maximum.

$$mid = \frac{min+max}{2}$$

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by midrange_functor.

Range

The range is calculated by taking the difference of the maximum and the minimum.

range = max − min

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by range_functor.

Median

The median is calculated by taking the median of all values.

median = median{i1, i2, ..., in}

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by median_functor.

Interquartile Mean

The interquartile mean is calculated by calculating the arithmetic average of all values but discarding the lowest and highest 25 % .

$$iqm =\frac{2}{n}\sum_{k=\frac{1}{4}n}^{\frac{3}{4}n}i_k$$

The formula assumes that the ik are ordered. More information about the interquartile mean can be found in Wikipedia: http://en.wikipedia.org/wiki/Interquartile_mean.

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by iqm_functor.

Trimean

The trimean is calculated by calculating the root of the average of squares.

$$tm=\frac{i_{\frac{1}{4}n}+2i_{\frac{2}{4}n}+i_{\frac{3}{4}n}}{4}$$

The formula assumes that the ik are ordered. $i_{\frac{1}{4}n}$ is the first quartile, $i_{\frac{2}{4}n}$ is the median and $i_{\frac{3}{4}n}$ is the third quartile. More information about the trimean can be found in Mathworld: http://mathworld.wolfram.com/Trimean.html.

For a code example see the samples under Arithmetic Mean, but replace arithmetic_mean_functor by trimean_functor.

I encourage you to study the binning sample and play with it to get a feel for the various ways of binning. Make sure to have a look at the different functors to see how they are implemented and how easy you could implement your own variants.