Introduction View Source

Run in Livebook

Install dependencies

Mix.install([
  {:kino, "~> 0.3.0"},
  {:vix, "~> 0.5"}
])
# print vips version
IO.puts("Version: " <> Vix.Vips.version())

Vips Image IO

All image IO operations such as reading and writing files are available in Vix.Vips.Image module. Image module also contains functions get image attributes. Most Vips operations takes %Vix.Vips.Image{} instance

alias Vix.Vips.Image

Reading image from a file. Note that image is not actually loaded to the memory at this point. img is %Image{} struct.

{:ok, %Image{} = img} = Image.new_from_file("~/Downloads/kitty.png")

Writing Image to a file. Image type selected based on the image path extension. See documentation for more options

:ok = Image.write_to_file(img, "kitty.jpg[Q=90]")
# let's print image dimensions
IO.puts("Width: #{Image.width(img)}")
IO.puts("Height: #{Image.height(img)}")

Kino supports showing image inline. We can use this to display image in the livebook. This opens gate for exploratory image processing

defmodule VixExt do
  alias Vix.Vips.Operation

  @max_height 500

  def show(%Image{} = image) do
    height = Image.height(image)

    # scale down if image height is larger than 500px
    image =
      if height > @max_height do
        Operation.resize!(image, @max_height / height)
      else
        image
      end

    # write vips-image as png image to memory
    {:ok, image_bin} = Image.write_to_buffer(image, ".png")
    Kino.render(Kino.Image.new(image_bin, "image/png"))

    :ok
  end
end
import VixExt

# Lets see show in action
show(img)

Vips Operations

All image processing operations are available in Vix.Vips.Operation

alias Vix.Vips.Operation

Crop

Getting a rectangular region from the image

{:ok, extract_img} = Operation.extract_area(img, 100, 50, 200, 200)
show(extract_img)

Thumbnail.

This function accepts many optional parameters, see vips thumbnail doc for more details.

Also see Operation.thumbnail/3 which accepts image path

{:ok, thumb} = Operation.thumbnail_image(img, 100)
show(thumb)

Resize

Resize image to 400x600. resize function accepts scaling factor. Skip vscale if you want to preserve aspect ratio

hscale = 400 / Image.width(img)
vscale = 600 / Image.height(img)

{:ok, resized_img} = Operation.resize(img, hscale, vscale: vscale)
show(resized_img)

Flip

direction =
  case IO.gets("Direction: ") do
    "horizontal\n" -> :VIPS_DIRECTION_HORIZONTAL
    "vertical\n" -> :VIPS_DIRECTION_VERTICAL
  end

{:ok, flipped_img} = Operation.flip(img, direction)
show(flipped_img)

Text

Text operation takes multiple optional parameters. See libvips doc for more details

str = String.trim(IO.gets("Text: "))
{:ok, {text, _}} = Operation.text(str, dpi: 300, rgba: true)

# add text to an image
{:ok, inserted_text_img} = Operation.composite2(img, text, :VIPS_BLEND_MODE_OVER, x: 50, y: 20)

show(inserted_text_img)

Creating GIF

black = Operation.black!(500, 500, bands: 3)

# create images with different grayscale
frames = Enum.map(1..255//10, fn n ->
  Operation.linear!(black, [1], [n,n,n])
end)

{:ok, joined_img} = Operation.arrayjoin(frames, across: 1)

{:ok, joined_img} =
  Image.mutate(joined_img, fn mut_img ->
    frame_delay = List.duplicate(100, length(frames))
    :ok = Vix.Vips.MutableImage.set(mut_img, "delay", :VipsArrayInt, frame_delay)
  end)

:ok = Operation.gifsave(joined_img, Path.expand("~/Downloads/bw.gif"), "page-height": 500)

Few more operations

# Gaussian blur
{:ok, blurred_img} = Operation.gaussblur(img, 5)
show(blurred_img)

# convert image to a grayscale image
{:ok, bw_img} = Operation.colourspace(img, :VIPS_INTERPRETATION_B_W)
show(bw_img)

# adding gray border
{:ok, extended_img} =
  Operation.embed(img, 10, 10, Image.width(img) + 20, Image.height(img) + 20,
    extend: :VIPS_EXTEND_BACKGROUND,
    background: [128]
  )

show(extended_img)

# rotate image 90 degree clockwise
{:ok, rotated_img} = Operation.rot(img, :VIPS_ANGLE_D90)
show(rotated_img)

# join two images horizontally
{:ok, main_img} = Image.new_from_file("~/Downloads/kitten.svg")
{:ok, joined_img} = Operation.join(img, main_img, :VIPS_DIRECTION_HORIZONTAL, expand: true)
show(joined_img)