Area processing steps#

Area processing steps use a neighborhood of samples and therefore a non-zero local halo (the halo describes the neighborhood). They require the Shared-memory API. Some area processing steps may also require temporary storage. Compose halo on ingest and on each neighborhood step with Halo operators (see also Halos and step order). Resize is included as an area processing step because it needs halo and changes tile geometry like other area family steps.

Box blur#

function::box_blur. Function<function::box_blur>() with BoxBlur<Width, Height> applies a NxM box filter (arithmetic mean) over the internal tile. Width and Height must be odd integers \(\geq\) 1. The required halo is (Width - 1) / 2 horizontally and (Height - 1) / 2 vertically.

using BoxBlur3x3 = decltype(
    Function<function::box_blur>() +
    BoxBlur<3, 3>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

Each output sample is the average of all samples in the Width x Height neighborhood. The cell-based implementation uses a sliding-window. Box blur is a common building block for multi-pass smoothing (for example, three or more passes approximate a wider Gaussian).

Warning

Width and Height must be odd integers \(\geq\) 1. BoxBlur enforces this at compile time with static_assert; even dimensions are rejected rather than accepted with a shifted kernel.

Gaussian blur#

function::gaussian_blur. Function<function::gaussian_blur>() with GaussianBlur<RadiusTenths[, TailWidth]> applies a discrete Gaussian FIR (Finite Impulse Response) filter over the internal tile. RadiusTenths is an integer from 1 to 100, giving a pixel radius of 0.1 to 10.0 in steps of 0.1 (for example 20 \(\rightarrow\) pixel radius 2.0). (As a rule of thumb, a pixel radius of 1.0 tends to remove detail that is 1.0 pixel or less in size.)

Optional TailWidth selects neighborhood size for the same \(\sigma\) (default gaussian_tail_width::standard):

  • standard: discrete Gaussian with \(\sigma\) = RadiusTenths/10; neighborhood sized for approximately one \(\sigma\) of support.

  • wide: at the same pixel radius, \(\sigma\) = RadiusTenths/10; larger support neighborhood (approximately two \(\sigma\) of support). The extra support improves accuracy by retaining more of the Gaussian tails. wide requires RadiusTenths \(\leq\) 50 so the neighborhood stays within the library maximum of 21x21.

Neighborhood size grows with pixel radius; halo is half the neighborhood width. For wide, the neighborhood is about twice as wide as standard for the same RadiusTenths (for example 35 is 9x9 for standard and 17x17 for wide).

Range RadiusTenths

Neighborhood standard

Neighborhood wide

Actual pixel radius (\(\sigma\))

1–9

3x3

5x5

0.1–0.9

10–20

5x5

9x9

1.0–2.0

21–30

7x7

13x13

2.1–3.0

31–40

9x9

17x17

3.1–4.0

41–50

11x11

21x21

4.1–5.0

51–60

13x13

n/a (wide not supported)

5.1–6.0

61–70

15x15

n/a

6.1–7.0

71–80

17x17

n/a

7.1–8.0

81–90

19x19

n/a

8.1–9.0

91–100

21x21

n/a

9.1–10.0

// Radius 2.0, standard neighborhood, requires 2 halo on each side
using Gaussian2 = decltype(
    Function<function::gaussian_blur>() +
    GaussianBlur<20>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

// Radius 3.5, same sigma, wide neighborhood (17x17), halo 8
using GaussianWide35 = decltype(
    Function<function::gaussian_blur>() +
    GaussianBlur<35, gaussian_tail_width::wide>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

Three or more passes of box blur can approximate a Gaussian (see box blur above); gaussian_blur uses an explicit FIR instead. See 03_area_operation/gaussian_blur_fused_conversion.cu.

Median filter#

function::median. Function<function::median>() with Median<median_radius:: ...> selects the median of a fixed neighborhood on the internal tile. NPPDx distinguishes square windows (full rectangular kernels) from round windows (plus-shaped or corner-trimmed neighborhoods).

The round medians are slightly faster and more isotropic than the square medians and their diagonal artifacts are minimized.

median_radius

Shape

Samples

Neighborhood

r0_5

Round

5

Plus (cross): center pixel and its four axis neighbors in a 3x3 footprint.

r1_0

Square

9

3x3 Median filter. NPP 3x3 median equivalent; see 03_area_operation/median_fused_conversion.cu.

r1_5

Round

21

Round 5x5: all positions in a 5x5 square except the four corners.

r2_0

Square

25

5x5 Median Filter. NPP 5x5 median equivalent.

Halo size follows the extent of the neighborhood: 1 pixel for r0_5 and r1_0; 2 pixels for r1_5 and r2_0.

// Square 3x3 median
using Median3x3 = decltype(
    Function<function::median>() +
    Median<median_radius::r1_0>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

// Round 21-sample median (5x5 minus corners)
using MedianRound = decltype(
    Function<function::median>() +
    Median<median_radius::r1_5>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

Sharpen#

function::sharpen. Function<function::sharpen>() with Sharpen<Weights> applies a 3x3 convolutional sharpen on the internal tile. Weights is a type with compile-time corner_w, side_w, and center_w members that map to a symmetric kernel:

[  A   B   A  ]
[  B   C   B  ]          4*A + 4*B + C = 1
[  A   B   A  ]

Valid sharpen kernels satisfy 4*A + 4*B + C = 1 (DC gain unity). Two common integer presets, shown side by side:

4-Neighbor Laplacian      8-Neighbor Laplacian
 [   0  -1   0  ]          [  -1  -1  -1  ]
 [  -1   5  -1  ]          [  -1   9  -1  ]
 [   0  -1   0  ]          [  -1  -1  -1  ]

 (A=0, B=-1, C=5)          (A=-1, B=-1, C=9)

Express either preset with a Weights type whose corner_w, side_w, and center_w match the table above, then Sharpen<Weights>().

The built-in sharpen_weights::rosenfeld_generalized_weights<WeightPercent> implements a generalized additive Rosenfeld Laplacian. WeightPercent (0–100) interpolates between identity and a 50%-strength kernel. The isotropic Rosenfeld kernel is designed to treat diagonal edges equally to horizontal and vertical edges, unlike the two examples above.

Isotropic Rosenfeld at 50% (rosenfeld_50_weights)
    [  -1/24  -2/24  -1/24  ]
    [  -2/24  36/24  -2/24  ]
    [  -1/24  -2/24  -1/24  ]

   (A=-1/24, B=-2/24, C=36/24)
  • At 0%: The kernel is identity (no sharpening).

  • At 50%: The weights are (-1/24, -2/24, 36/24) for corner, side, and center respectively – the default rosenfeld_50_weights alias.

  • Coefficients satisfy the rule: 4 * corner_w + 4 * side_w + center_w = 1.

The required halo is 1 pixel in each direction.

using Sharpen50 = decltype(
    Function<function::sharpen>() +
    Sharpen<sharpen_weights::rosenfeld_generalized_weights<50>>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

// Custom Rosenfeld strength (e.g. 30%)
using Sharpen30 = decltype(
    Function<function::sharpen>() +
    Sharpen<sharpen_weights::rosenfeld_generalized_weights<30>>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

Other presets in sharpen_weights include identity_weights (no-op) and binomial_3x3_weights (low-pass binomial/Gaussian kernel).

Resize#

function::resize. Function<function::resize>() with Resize<J, K, Method> performs a resize of J input samples to K output samples along each axis (coprime J and K). Method is an interpolation_method:

  • Nearest neighbor nearest: Selects the nearest input sample; no blurring but blocky or pixellated results when going larger, has aliasing artifacts when going smaller. No halo required.

  • Bilinear interpolation bilinear: Linear interpolation on both axes. Smooth linear blend of neighboring samples. Best used for integer scaling factors.

  • Mitchell-Netravali cubic kernel bicubic: Catmull-Rom (B=0, C=0.5). Smoother than bilinear; loses detail near Nyquist but adds some sharpening to make up for it.

  • Lanczos-3 lanczos3: Windowed sinc function (sinc(x)*sinc(x/3)). Preserves high frequency details the best of these methods. Requires a large halo especially when downscaling.

Resize typically needs a non-zero local halo that depends on the interpolation kernel, and it changes output tile geometry relative to the input. Refer to resize-related storage traits (for example output_storage_of_t and temp_storage_of_t) and size the grid from the output dimensions. See Shared-memory storage traits and 04_resize/resize_example.cu.

using Resize2x = decltype(
    Function<function::resize>() +
    Resize<1, 2, interpolation_method::bilinear>() +
    TileSize<48, 48>() +
    Block() +
    SM<900>());

See also 04_resize/fused_resize.cu for resize fused with box blur and color convert.

See also#