# `Image.YUV`
[🔗](https://github.com/elixir-image/image/blob/v0.72.0/lib/image/yuv.ex#L1)

Functions to convert from/to YUV (YCbCr) encoding and BT.601/BT.709
colorspaces and sRGB images.

The following YUV ([YCbCr](https://en.wikipedia.org/wiki/YCbCr)) binary
formats are supported:

* Planar frame types only (not packed frames).

* `4:4:4`, `4:2:2` and `4:2:0` encodings.

* [BT.601](https://en.wikipedia.org/wiki/Rec._601) and
  [BT.709](https://en.wikipedia.org/wiki/Rec._709) colorspaces.

* `:limited` (studio-swing, the default) and `:full` (PC/JPEG)
  signal ranges. See `t:Image.YUV.yuv_range/0`.

Performance profiling indicates this implementation is not suitable
for real time frame processing of YUV images.

# `yuv_colorspace`

```elixir
@type yuv_colorspace() :: :bt601 | :bt709
```

YUV colorspace

# `yuv_encoding`

```elixir
@type yuv_encoding() :: :C444 | :C422 | :C420
```

YUV encoding

# `yuv_list`

```elixir
@type yuv_list() :: [binary()]
```

YUV data as a three-element list of binaries

# `yuv_range`

```elixir
@type yuv_range() :: :limited | :full
```

YUV signal range.

* `:limited` (also called studio-swing or TV range) encodes luma in
  `16..235` and chroma in `16..240`. This is the convention used by
  most video formats (including YUV4MPEG) and is the default.

* `:full` (also called PC range or JPEG range) encodes luma and chroma
  across the whole `0..255` range.

The range must match on encode and decode; mixing them shifts colors
(a full-range signal decoded as limited-range washes toward grey, and
vice versa).

# `decode`
*since 0.41.0* 

Deocdes a raw YUV binary into `[y, u, v]` planes
where each plane is a binary.

### Arguments

* `binary` is a binary representation of a YUV image.

* `width` is the width of the image encoded in `yuv`.

* `height` is the height of the image encoded in `yuv`.

* `encoding` is one of `:C444`, `:C422` or
  `:C420` representing how `yuv` is encoded.

### Returns

* `{:ok, [y, u, v]}` or

* `{:error, reason}`.

### Examples

    iex> binary = :binary.copy(<<128>>, 8 * 8 + 2 * (4 * 4))
    iex> {:ok, [y, u, v]} = Image.YUV.decode(binary, 8, 8, :C420)
    iex> {byte_size(y), byte_size(u), byte_size(v)}
    {64, 16, 16}

    iex> {:error, %Image.Error{}} = Image.YUV.decode(<<1, 2, 3>>, 8, 8, :C444)

# `encode`
*since 0.41.0* 

```elixir
@spec encode(image :: Vix.Vips.Image.t(), encoding :: yuv_encoding()) ::
  {:ok, yuv_list()} | {:error, Image.error()}
```

Encodes an image that is in a YUV colorspace to
raw YUV data that is a list of the three planes, each a
binary.

The data is always written in a planar format.

### Arguments

* `image` is any `t:Vimage.t/0` that is in a YUV
  colorspace such as that returned from `Image.YUV.new_from_file/5`
  or `Image.YUV.new_from_binary/5`.

* `encoding` is one of `:C444`, `:C422` or
  `:C420` representing how `yuv` is to be encoded.

### Returns

* `{:ok, [y, u, v]}` or

* `{:error, Image.error()}`.

### Examples

    iex> yuv_image = Image.new!(8, 8, color: [128, 128, 128])
    iex> {:ok, [y, u, v]} = Image.YUV.encode(yuv_image, :C422)
    iex> {byte_size(y), byte_size(u), byte_size(v)}
    {64, 32, 32}

# `new_from_binary`
*since 0.41.0* 

```elixir
@spec new_from_binary(
  binary :: binary(),
  width :: pos_integer(),
  height :: pos_integer(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: {:ok, Vix.Vips.Image.t()} | {:error, Image.error()}
```

Converts raw YUV data into an RGB image.

The data is assumed, and required to be in:

* Planar format
* 8-bit color depth

### Arguments

* `binary` is raw YUV data as a binary.

* `width` is the width of the image encoded in
  the YUV data.

* `height` is the height of the image encoded in
  the YUV data.

* `encoding` is one of `:C444`, `:C422` or
  `:C420`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709`.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`. It must match the range used
  when the data was encoded.

### Returns

* `{:ok, rgb_image}` or

* `{:error, reason}`.

### Examples

    iex> binary = :binary.copy(<<128>>, 3 * 8 * 8)
    iex> {:ok, image} = Image.YUV.new_from_binary(binary, 8, 8, :C444)
    iex> {Image.shape(image), Image.colorspace(image)}
    {{8, 8, 3}, :srgb}

# `new_from_file`
*since 0.41.0* 

```elixir
@spec new_from_file(
  path :: Path.t(),
  width :: pos_integer(),
  height :: pos_integer(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: {:ok, Vix.Vips.Image.t()} | {:error, Image.error()}
```

Converts the raw YUV data in a `.yuv` file
into an RGB image.

The data is assumed, and required to be in:

* Planar format
* 8-bit color depth

### Arguments

* `path` is any accessible file system path.

* `width` is the width of the image encoded in
  the YUV data.

* `height` is the height of the image encoded in
  the YUV data.

* `encoding` is one of `:C444`, `:C422` or
  `:C420`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709`.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`. It must match the range used
  when the data was encoded.

### Returns

* `{:ok, rgb_image}` or

* `{:error, reason}`.

### Examples

    iex> image = Image.new!(8, 8, color: :green)
    iex> path = Path.join(System.tmp_dir!(), "yuv_new_from_file_doctest.yuv")
    iex> :ok = Image.YUV.write_to_file(image, path, :C420)
    iex> {:ok, rgb_image} = Image.YUV.new_from_file(path, 8, 8, :C420)
    iex> File.rm(path)
    :ok
    iex> Image.shape(rgb_image)
    {8, 8, 3}

# `to_rgb`
*since 0.41.0* 

```elixir
@spec to_rgb(
  image :: Vix.Vips.Image.t(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) ::
  {:ok, Vix.Vips.Image.t()} | {:error, Image.error()}
```

Convert an image in an YUV colorspace and convert it to RGB
colorspace.

### Arguments

* `image` is any `t:Vimage.t/0` that is in a YUV
  colorspace such as that returned from `Image.YUV.new_from_file/5`
  or `Image.YUV.new_from_binary/5`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709` that represents the colorspace of `image` before
  conversion.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`.

### Examples

    iex> yuv_image = Image.new!(8, 8, color: [128, 128, 128])
    iex> {:ok, rgb_image} = Image.YUV.to_rgb(yuv_image, :bt601)
    iex> {Image.shape(rgb_image), Image.colorspace(rgb_image)}
    {{8, 8, 3}, :srgb}

# `to_rgb`
*since 0.41.0* 

```elixir
@spec to_rgb(
  yuv :: yuv_list(),
  width :: pos_integer(),
  height :: pos_integer(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: {:ok, Vix.Vips.Image.t()} | {:error, Image.error()}
```

Takes the `[y, u, v]` planes and converts them to
an RGB image.

### Arguments

* `yuv` is a list of three binaries representing the `Y`,
  `U` and `V` planes. Such a list is returned from
  `Image.YUV.to_yuv/3` and from `Image.YUV.encode/2`.

* `width` is the width of the image encoded in `yuv`.

* `height` is the height of the image encoded in `yuv`.

* `encoding` is one of `:C444`, `:C422` or
  `:C420` representing how `yuv` is encoded.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709` that represents the colorspace of `image` before
  conversion.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`.

### Returns

* `{:ok, image}` or

* `{:error, reason}`.

### Examples

    iex> y = :binary.copy(<<235>>, 64)
    iex> u = :binary.copy(<<128>>, 64)
    iex> v = :binary.copy(<<128>>, 64)
    iex> {:ok, image} = Image.YUV.to_rgb([y, u, v], 8, 8, :C444, :bt601)
    iex> Image.shape(image)
    {8, 8, 3}

# `to_yuv`
*since 0.41.0* 

```elixir
@spec to_yuv(
  image :: Vix.Vips.Image.t(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: {:ok, yuv_list()} | {:error, Image.error()}
```

Converts an image to raw YUV data as
a binary.

### Arguments

* `image` is any `t:Vimage.t/0`.

* `encoding` is one of `:C444`, `:C422` or
  `:C420`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709`.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`.

### Returns

* `{:ok, [y, u, v]}` or

* `{:error, reason}`.

### Examples

    iex> image = Image.new!(8, 8, color: :red)
    iex> {:ok, [y, u, v]} = Image.YUV.to_yuv(image, :C420)
    iex> {byte_size(y), byte_size(u), byte_size(v)}
    {64, 16, 16}

# `valid_colorspaces`

```elixir
@spec valid_colorspaces() :: [:bt601 | :bt709]
```

Returns the list of YUV → RGB conversion colorspaces supported by
this module.

### Examples

    iex> Image.YUV.valid_colorspaces()
    [:bt601, :bt709]

# `valid_encodings`

```elixir
@spec valid_encodings() :: [:C444 | :C422 | :C420]
```

Returns the list of YUV chroma-subsampling encodings supported by
this module.

### Examples

    iex> Image.YUV.valid_encodings()
    [:C444, :C422, :C420]

# `valid_ranges`

```elixir
@spec valid_ranges() :: [:limited | :full]
```

Returns the list of YUV signal ranges supported by this module.

See `t:Image.YUV.yuv_range/0` for the meaning of each range.

### Examples

    iex> Image.YUV.valid_ranges()
    [:limited, :full]

# `write_to_binary`
*since 0.41.0* 

```elixir
@spec write_to_binary(
  image :: Vix.Vips.Image.t(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: {:ok, binary()} | {:error, Image.error()}
```

Writes an image to a YUV raw binary.

### Arguments

* `image` is any `t:Vimage.t/0`.

* `encoding` is one of `:C444`, `:C422` or
  `:C420`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709`.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`.

### Returns

* `{:ok, yuv_binary}` or

* `{:error, reason}`.

### Examples

    iex> image = Image.new!(8, 8, color: :green)
    iex> {:ok, binary} = Image.YUV.write_to_binary(image, :C420)
    iex> byte_size(binary)
    96

# `write_to_file`
*since 0.41.0* 

```elixir
@spec write_to_file(
  image :: Vix.Vips.Image.t(),
  path :: Path.t(),
  encoding :: yuv_encoding(),
  colorspace :: yuv_colorspace(),
  range :: yuv_range()
) :: :ok | {:error, Image.error()}
```

Writes an image to a YUV file as raw YUV data.

It is recommeneded, but not required, that the path
name use a `.yuv` suffix.

### Arguments

* `path` is any accessible file system path.

* `encoding` is one of `:C444`, `:C422` or
  `:C420`.

* `colorspace` is one of `:bt601` (the default) or
  `:bt709`.

* `range` is one of `:limited` (the default) or `:full`.
  See `t:Image.YUV.yuv_range/0`.

### Returns

* `:ok` or

* `{:error, reason}`.

### Examples

    iex> image = Image.new!(8, 8, color: :green)
    iex> path = Path.join(System.tmp_dir!(), "yuv_write_to_file_doctest.yuv")
    iex> Image.YUV.write_to_file(image, path, :C420)
    :ok
    iex> File.rm(path)
    :ok

---

*Consult [api-reference.md](api-reference.md) for complete listing*
