---
name: r.smooth.edgepreserve
description: Smoothing with anisotropic diffusion
keywords: [ raster, smoothing, edge detection, parallel, denoise ]
---

# r.smooth.edgepreserve

Smoothing with anisotropic diffusion

=== "Command line"

    **r.smooth.edgepreserve**
    [**-p**]
    **input**=*name*
    **output**=*name*
    **threshold**=*float*
    **lambda**=*float*
    **steps**=*integer*
    **function**=*string*
    [**memory**=*memory in MB*]
    [**nprocs**=*integer*]
    [**--overwrite**]
    [**--verbose**]
    [**--quiet**]
    [**--qq**]
    [**--ui**]

    Example:

    ```sh
    r.smooth.edgepreserve input=name output=name threshold=5 lambda=0.1 steps=10 function=tukey
    ```

=== "Python (grass.tools)"

    *grass.tools.Tools.r_smooth_edgepreserve*(**input**,
        **output**,
        **threshold**=*5*,
        **lambda**=*0.1*,
        **steps**=*10*,
        **function**=*"tukey"*,
        **memory**=*300*,
        **nprocs**=*0*,
        **flags**=*None*,
        **overwrite**=*None*,
        **verbose**=*None*,
        **quiet**=*None*,
        **superquiet**=*None*)

    Example:

    ```python
    tools = Tools()
    tools.r_smooth_edgepreserve(input="name", output="name", threshold=5, lambda=0.1, steps=10, function="tukey")
    ```

    This grass.tools API is experimental in version 8.5 and expected to be stable in version 8.6.

=== "Python (grass.script)"

    *grass.script.run_command*("***r.smooth.edgepreserve***",
        **input**,
        **output**,
        **threshold**=*5*,
        **lambda**=*0.1*,
        **steps**=*10*,
        **function**=*"tukey"*,
        **memory**=*300*,
        **nprocs**=*0*,
        **flags**=*None*,
        **overwrite**=*None*,
        **verbose**=*None*,
        **quiet**=*None*,
        **superquiet**=*None*)

    Example:

    ```python
    gs.run_command("r.smooth.edgepreserve", input="name", output="name", threshold=5, lambda=0.1, steps=10, function="tukey")
    ```

## Parameters

=== "Command line"

    **input**=*name* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Name of input raster map  
    **output**=*name* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Name for output raster map  
    **threshold**=*float* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Gradient magnitude threshold (in map units)  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0.000000001-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *5*  
    **lambda**=*float* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Rate of diffusion  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0-1*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0.1*  
    **steps**=*integer* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of diffusion steps  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *1-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *10*  
    **function**=*string* **[required]**  
    &nbsp;&nbsp;&nbsp;&nbsp;Diffusivity function  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *exponential, quadratic, tukey*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *tukey*  
    **memory**=*memory in MB*  
    &nbsp;&nbsp;&nbsp;&nbsp;Maximum memory to be used (in MB)  
    &nbsp;&nbsp;&nbsp;&nbsp;Cache size for raster rows  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *300*  
    **nprocs**=*integer*  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of threads for parallel computing  
    &nbsp;&nbsp;&nbsp;&nbsp;0: use OpenMP default; &gt;0: use nprocs; &lt;0: use MAX-nprocs  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0*  
    **-p**  
    &nbsp;&nbsp;&nbsp;&nbsp;Preserve details with Tukey  
  
    **--overwrite**  
    &nbsp;&nbsp;&nbsp;&nbsp;Allow output files to overwrite existing files  
    **--help**  
    &nbsp;&nbsp;&nbsp;&nbsp;Print usage summary  
    **--verbose**  
    &nbsp;&nbsp;&nbsp;&nbsp;Verbose module output  
    **--quiet**  
    &nbsp;&nbsp;&nbsp;&nbsp;Quiet module output  
    **--qq**  
    &nbsp;&nbsp;&nbsp;&nbsp;Very quiet module output  
    **--ui**  
    &nbsp;&nbsp;&nbsp;&nbsp;Force launching GUI dialog

=== "Python (grass.tools)"

    **input** : str | np.ndarray, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Name of input raster map  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: input, raster, *name*  
    **output** : str | type(np.ndarray) | type(np.array) | type(gs.array.array), *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Name for output raster map  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: output, raster, *name*  
    **threshold** : float, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Gradient magnitude threshold (in map units)  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0.000000001-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *5*  
    **lambda** : float, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Rate of diffusion  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0-1*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0.1*  
    **steps** : int, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of diffusion steps  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *1-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *10*  
    **function** : str, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Diffusivity function  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *exponential, quadratic, tukey*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *tukey*  
    **memory** : int, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Maximum memory to be used (in MB)  
    &nbsp;&nbsp;&nbsp;&nbsp;Cache size for raster rows  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: *memory in MB*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *300*  
    **nprocs** : int, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of threads for parallel computing  
    &nbsp;&nbsp;&nbsp;&nbsp;0: use OpenMP default; &gt;0: use nprocs; &lt;0: use MAX-nprocs  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0*  
    **flags** : str, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *p*  
    &nbsp;&nbsp;&nbsp;&nbsp;**p**  
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Preserve details with Tukey  
  
    **overwrite** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Allow output files to overwrite existing files  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **verbose** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Verbose module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **quiet** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Quiet module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **superquiet** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Very quiet module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  

    Returns:

    **result** : grass.tools.support.ToolResult | np.ndarray | tuple[np.ndarray] | None  
    If the tool produces text as standard output, a *ToolResult* object will be returned. Otherwise, `None` will be returned. If an array type (e.g., *np.ndarray*) is used for one of the raster outputs, the result will be an array and will have the shape corresponding to the computational region. If an array type is used for more than one raster output, the result will be a tuple of arrays.

    Raises:

    *grass.tools.ToolError*: When the tool ended with an error.

=== "Python (grass.script)"

    **input** : str, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Name of input raster map  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: input, raster, *name*  
    **output** : str, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Name for output raster map  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: output, raster, *name*  
    **threshold** : float, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Gradient magnitude threshold (in map units)  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0.000000001-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *5*  
    **lambda** : float, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Rate of diffusion  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *0-1*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0.1*  
    **steps** : int, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of diffusion steps  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *1-*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *10*  
    **function** : str, *required*  
    &nbsp;&nbsp;&nbsp;&nbsp;Diffusivity function  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *exponential, quadratic, tukey*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *tukey*  
    **memory** : int, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Maximum memory to be used (in MB)  
    &nbsp;&nbsp;&nbsp;&nbsp;Cache size for raster rows  
    &nbsp;&nbsp;&nbsp;&nbsp;Used as: *memory in MB*  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *300*  
    **nprocs** : int, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Number of threads for parallel computing  
    &nbsp;&nbsp;&nbsp;&nbsp;0: use OpenMP default; &gt;0: use nprocs; &lt;0: use MAX-nprocs  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *0*  
    **flags** : str, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Allowed values: *p*  
    &nbsp;&nbsp;&nbsp;&nbsp;**p**  
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Preserve details with Tukey  
  
    **overwrite** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Allow output files to overwrite existing files  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **verbose** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Verbose module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **quiet** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Quiet module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*  
    **superquiet** : bool, *optional*  
    &nbsp;&nbsp;&nbsp;&nbsp;Very quiet module output  
    &nbsp;&nbsp;&nbsp;&nbsp;Default: *None*

## DESCRIPTION

This module performs adaptive, edge-preserving raster smoothing
using anisotropic diffusion. The result is a denoised raster that
retains important features, especially edges. Unlike traditional
smoothing methods (such as a uniform average filter, like the one available
in *r.neighbors*), this module smooths only areas with similar values
and works to preserve sharp transitions between different regions.

![An example of smoothed image](r_smooth_edgepreserve.jpg)  
*Figure: "A" is the original (unsmoothed) raster and
"B" is the smoothed version (with quite agressive settings to emphasize
smoothing effects).*

The module supports three types of diffusivity (conductance) functions:

- **Exponential**
- **Quadratic**  
  *(both as proposed by Perona & Malik, 1990)*
- **Tukey’s biweight**  
  *(as proposed by Black et al., 1998)*

The **Tukey’s biweight** function is more aggressive than the others and
tends to produce large, smoothly blended areas. This makes it
particularly useful for raster segmentation. If you prefer a gentler
effect that preserves smaller features, you can use the **`-p`** flag,
which switches to a softer variant of the Tukey function — ideal for
applications like visualization.

### Parameters

- **`threshold`**  
  Defines how small a difference in raster values should be treated as
  noise. The unit depends on the raster (e.g., digital numbers, meters,
  etc.), and should be adjusted individually for each map.

- **`lambda`**  
  Controls the smoothing rate.
  - For **exponential** and **quadratic** functions, the value is
  automatically clamped between `0` and `0.25` for numeric stability.
  - For **Tukey**, the full range is used.

- **`steps`**  
  Specifies the number of smoothing iterations. Higher values result
  in stronger smoothing.  
  Note that:
  - Excessive iterations can cause loss of fine detail.
  - Detail preservation improves in this order:  
    `quadratic` → `exponential` → `Tukey` → `Tukey` with **`-p`** flag.

## NOTES

Internally calculations are performed in double precision and only at the end
converted to the same type as the input map.

The module will try to keep all temporary data in RAM. Thus it is important
to set the **`memory`** parameter as high as possible.  If the map does
not fit into RAM, a temporary file will be used instead.

The original *Perona & Malik* paper uses von Neumann (4-connected)
neighbourhood for value calculation, but this module uses Moore
(8-connected) neighbourhood. Computed gradients of neighbouring cells
are adjusted to equalise distances for diagonals and non-square cells.

## EXAMPLES

```sh
# Set computational region to orthophoto map
g.region raster=ortho_2001_t792_1m -p

# Smooth with average in a 3x3 window. Note how all edges have became blurry
# but at the same time streets and roofs are still noisy
r.neighbors input=ortho_2001_t792_1m output=ortho_smoothed_avg \
 size=3 method=average

# Smooth with median in a 3x3 window. Although better than average smoothing,
# thin lines still are lost and noise on streets and roofs is still present.
r.neighbors input=ortho_2001_t792_1m output=ortho_smoothed_med \
 size=3 method=median

# Smooth with quadratic diffusivity function. Note better preservation of
# small details and reduction of noise on streets and roofs.
r.smooth.edgepreserve function=quadratic input=ortho_2001_t792_1m \
 output=ortho_smoothed_qa threshold=15 lambda=0.4 steps=20

# Smooth with exponential diffusivity function. Even better edge delineation
# but at the same time increase of noise in really noisy areas.
r.smooth.edgepreserve function=exponential input=ortho_2001_t792_1m \
 output=ortho_smoothed_ex threshold=15 lambda=0.4 steps=20

# Smooth with aggressive Tukey's diffusivity function. Better preservation of
# minor details e.g. as road markings but little smoothing in areas with
# fine, well expressed texture.
r.smooth.edgepreserve function=tukey input=ortho_2001_t792_1m \
 output=ortho_smoothed_ta threshold=15 lambda=0.4 steps=20

# Smooth with preserving Tukey's diffusivity function. Only low noise areas
# have been smoothed.
r.smooth.edgepreserve function=tukey input=ortho_2001_t792_1m \
 output=ortho_smoothed_tp threshold=15 lambda=0.4 steps=20 -p
```

## SEE ALSO

- Smooth with statistics: *[r.neighbours](r.neighbours)*
- The Mumford-Shah variational model for image segmentation (an add-on):
*[r.smooth.seg](https://grass.osgeo.org/grass-stable/manuals/addons/r.smooth.seg.html)*

## REFERENCES

- Perona P. and Malik J. 1990. Scale-space and edge detection using anisotropic
diffusion. *IEEE transactions on pattern analysis and machine intelligence*,
12(7).
- Black M.J., Sapiro G., Marimont D.H. and Heeger D. 1998. Robust anisotropic
diffusion. *IEEE transactions on image processing*, 7(3).

## AUTHOR

Maris Nartiss, University of Latvia.

## SOURCE CODE

Available at: [r.smooth.edgepreserve source code](https://github.com/OSGeo/grass/tree/main/raster/r.smooth.edgepreserve)
([history](https://github.com/OSGeo/grass/commits/main/raster/r.smooth.edgepreserve))  
Latest change: Saturday Oct 25 22:50:58 2025 in commit [ee87f21](https://github.com/OSGeo/grass/commit/ee87f21833475fd40805f58d1702f7d64478a27b)
