Packages

A Plug-based image optimization server with a declarative path API.

Current section

Files

Jump to
image_pipe docs processing effects.md
Raw

docs/processing/effects.md

# Effects

Effects filter the image, adjust its colors, or lay a color over it, with or
without resizing.

Every effect is off unless the [group](../requesting-images.md#processing-groups)
sets it, and each group starts with all effects off again. Effects that take a
color accept a hex value or a CSS name, as described under
[colors](../requesting-images.md#colors). On a grayscale image, `monochrome`,
`duotone`, `colorize`, and `gradient` produce a color image only if one of
their colors isn't a neutral gray. The default `monochrome` and `duotone`
colors keep it gray.

On an original with an embedded color profile, such as Display P3, `gray` and
`bitonal` convert the image to sRGB first, and the other effects work in that
profile. Colors you give in the URL come out exactly as given. Tones mixed
from them, such as the middle tones of `duotone`, are mixed in the original's
profile, so they can differ slightly from the same effect on an sRGB image.

## Effect order

Effects run after resizing and cropping, in this order, whatever order the URL
lists them in:

`blur`, `progressive-blur`, `sharpen`, `pixelate`, `gray`, `bitonal`,
`monochrome`, `duotone`, `brightness`, `contrast`, `saturation`, `colorize`,
`gradient`

To run them in another order, put them in separate groups. This URL adjusts
brightness after contrast:

<!-- tabs-open -->

### URL

```text
/contrast=2/-/brightness=30/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.new()
|> ImagePipe.URL.group(contrast: 2)
|> ImagePipe.URL.group(brightness: 30)
```

<!-- tabs-close -->

Where effects sit among the other stages is described under
[processing order](../processing.md#processing-order).

## Filters

### blur

Accepts a [number](../requesting-images.md#numbers) from `0` to `1000`, the
Gaussian blur sigma. Default: none. `blur=0` has no effect.

Blurs the whole image. Larger values blur more. The sigma is in output pixels,
so [`dpr`](resize.md#dpr) doesn't change it. On a large image, a large sigma can
exceed the limit set in the server's configuration
([`max_blur_work`](../deployment.md#limiting-blur-and-sharpen-work)) and
fail with `422`. That limit covers all the blurs and sharpens in a request
together.

<!-- tabs-open -->

### URL

```text
/w=800/blur=2/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, resize: [width: 800], blur: 2)
```

<!-- tabs-close -->

### progressive-blur

Accepts `sigma,direction,start,stop`. Only the sigma is required:

- `sigma` is the maximum blur, a number from `0` to `1000`. `0` has no effect.
- `direction` is `down` (default), `left`, `up`, or `right`, or an angle in
  degrees, as described under [gradient](#gradient).
- `start` and `stop` are [fractions](../requesting-images.md#fractions) of the
  distance across the image, in that direction. They default to `0` and `1`.

Default: none. The image is sharp before `start`, and the blur grows to the full
sigma at `stop`. Swapping `start` and `stop` reverses the ramp, and equal values
switch from sharp to fully blurred in one step. The sigma is in output pixels,
so `dpr` doesn't change it. A progressive blur is slower than `blur`, so on a
large image it exceeds the limit set in the server's configuration
([`max_blur_work`](../deployment.md#limiting-blur-and-sharpen-work)) at a
smaller sigma and fails with `422`.

The values are positional, so you can't skip one: to set `start`, also write a
direction, as in `progressive-blur=4,down,0.5`. An empty value, such as
`progressive-blur=4,,0.5`, fails with `400`.

<!-- tabs-open -->

### URL

```text
/progressive-blur=4,down,0.2,0.8/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, progressive_blur: [sigma: 4, direction: :down, start: 0.2, stop: 0.8])
```

<!-- tabs-close -->

### sharpen

Accepts a number from `0.000001` to `10`, the sharpening sigma, or `0` to
disable the effect. Default: none.

Sharpens edges. The sigma is in output pixels, so `dpr` doesn't change it.
A sharpen counts toward the same limit as blurs
([`max_blur_work`](../deployment.md#limiting-blur-and-sharpen-work)).

<!-- tabs-open -->

### URL

```text
/w=400/sharpen=1.5/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, resize: [width: 400], sharpen: 1.5)
```

<!-- tabs-close -->

### pixelate

Accepts a whole number from `1` to `2147483647`, the block size in pixels. Default: none.
`pixelate=1` has no effect.

Replaces the image with square blocks of one color each. The block size is in
output pixels, so `dpr` doesn't change it. `pixelate=0` fails with `400`.

<!-- tabs-open -->

### URL

```text
/pixelate=8/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, pixelate: 8)
```

<!-- tabs-close -->

## Color adjustments

### gray

A [boolean](../requesting-images.md#booleans). Default: `false`.

Converts the image to grayscale. Transparency is kept.

<!-- tabs-open -->

### URL

```text
/gray/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, gray: true)
```

<!-- tabs-close -->

### bitonal

A [boolean](../requesting-images.md#booleans). Default: `false`.

Converts the image to pure black and white: pixels darker than middle gray (128
on a 0 to 255 scale) become black, and the rest become white. Transparency is
kept, including partial transparency.

<!-- tabs-open -->

### URL

```text
/bitonal/src/scans/receipt.png
```

### Elixir

```elixir
ImagePipe.URL.group(builder, bitonal: true)
```

<!-- tabs-close -->

### monochrome

Accepts `intensity,color`:

- `intensity` is a fraction. `0` has no effect, and `1` applies the full
  effect.
- `color` is optional and defaults to `b3b3b3`, a light gray.

Default: none. Recolors the image in shades of one color, from black in the
shadows to `color` in the highlights.

<!-- tabs-open -->

### URL

```text
/monochrome=0.8,704214/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, monochrome: [intensity: 0.8, color: "704214"])
```

<!-- tabs-close -->

### duotone

Accepts `intensity`, `intensity,shadow`, or `intensity,shadow,highlight`:

- `intensity` is a fraction. `0` has no effect, and `1` applies the full
  effect.
- `shadow` and `highlight` are colors. They default to black and white.

Default: none. Recolors the image with two colors: dark areas take the shadow
color and light areas the highlight color.

<!-- tabs-open -->

### URL

```text
/duotone=1,123456,efab89/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, duotone: [intensity: 1, shadow: "123456", highlight: "efab89"])
```

<!-- tabs-close -->

### brightness

Accepts a whole number from `-255` to `255`. Default: none. `brightness=0` has
no effect.

Adds the value to every color channel on a 0 to 255 scale. A 16-bit image
shifts by the same fraction of its range, so `brightness=40` lightens it as
much as an 8-bit image. Positive values lighten
the image and negative values darken it.

<!-- tabs-open -->

### URL

```text
/brightness=-20/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, brightness: -20)
```

<!-- tabs-close -->

### contrast

Accepts a number greater than 0, a contrast factor with no upper limit.
Default: none. `contrast=1` has no effect.

Multiplies every color channel's distance from 128 on a 0 to 255 scale, so
mid-gray keeps its value. With `contrast=2`, 64 becomes 0 and 192 clips to 255.
A 16-bit image scales around the same point in its range. Values above 1
increase contrast, and values below 1 reduce it. `contrast=0` fails with `400`.

<!-- tabs-open -->

### URL

```text
/contrast=1.25/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, contrast: 1.25)
```

<!-- tabs-close -->

### saturation

Accepts a number greater than 0, a saturation factor with no upper limit.
Default: none. `saturation=1` has no effect.

Values above 1 make colors more vivid, and values below 1 make them duller.
Use [`gray`](#gray) for full grayscale, since `saturation=0` fails with `400`.

<!-- tabs-open -->

### URL

```text
/saturation=0.7/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, saturation: 0.7)
```

<!-- tabs-close -->

## Color overlays

### colorize

Accepts `opacity,color` or `opacity,color,keep-alpha`:

- `opacity` is a fraction. `0` has no effect.
- `color` is required.
- `keep-alpha` is optional and keeps the image's transparency.

Default: none. Blends the color evenly over the whole image at the given
opacity. Without `keep-alpha`, the result is opaque. Transparent and partly
transparent areas are flattened onto white before the color is blended in.

<!-- tabs-open -->

### URL

```text
/colorize=0.3,red,keep-alpha/src/logos/brand.png
```

### Elixir

```elixir
ImagePipe.URL.group(builder, colorize: [opacity: 0.3, color: "red", keep_alpha: true])
```

<!-- tabs-close -->

### gradient

Accepts `opacity,color,direction,start,stop`:

- `opacity` is a fraction, the strength of the color at the end of the ramp.
  `0` has no effect.
- `color` is required.
- `direction` is the way the color grows: `down` (default), `left`, `up`, or
  `right`. It can also be an angle in degrees, clockwise from `down`, so `90`
  is `left` and `180` is `up`. Angles may be negative or decimal and wrap
  around at 360.
- `start` and `stop` are fractions of the distance across the image, in that
  direction. They default to `0` and `1`.

Default: none. Lays the color over the image, transparent before `start` and
reaching full opacity at `stop`. Swapping `start` and `stop` reverses the ramp,
and equal values make a hard edge. The directions follow the image as it is
displayed after rotating, flipping, and resizing. The image's transparency is
kept.

The values are positional, so to set `start` you also write a direction. An
empty value, such as `gradient=0.8,black,,0.5`, fails with `400`.

<!-- tabs-open -->

### URL

```text
/gradient=0.8,black,down,0.2,0.9/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder,
  gradient: [opacity: 0.8, color: "black", direction: :down, start: 0.2, stop: 0.9]
)
```

<!-- tabs-close -->