Current section

Files

Jump to
cimg lib cimg.ex
Raw

lib/cimg.ex

defmodule CImg do
@moduledoc """
Light-weight image processing module in Elixir with CImg. This module aims to
create auxiliary routines for Deep Learning.
Note: It still has a few image processing functions currentrly.
### Design detail
The entity of the image handled by CImg is on the NIF side. On Elixir, the
reference to the image generated by NIF is stored in the CImg structure. You
cannot read out the pixels of the image and process it directly, instead you
can use the image processing functions provided in this module.
The image will be assigned to Erlang Resource by NIF, so the image will
automatically be subject to garbage collection when it is no longer in use.
This is a very important point. Some of the functions in this module
immutably rewrite the original image. I recommend you to make a duplicate of
the image before performing the image processing.
### Platform
It has been confirmed to work in the following OS environment.
- Windows MSYS2/MinGW64
- WSL2/Ubuntu 20.04
### Demo
There is a simple program in demo directory. You can do it by following the steps below.
```shell
$ cd demo
$ mix deps.get
$ mix run -e "CImgDemo.demo1"
```
Close the appaired window, and stop the demo program.
"""
alias __MODULE__
alias CImg.NIF
# image object
# :handle - Erlang resource object pointing to the CImg image.
defstruct handle: nil
defimpl Inspect do
import Inspect.Algebra
def inspect(cimg, opts) do
concat(["%CImg{", to_doc(CImg.shape(cimg), opts), ", handle: ", to_doc(cimg.handle, opts), "}"])
end
end
# import builder functions.
import CImg.Builder
@doc """
Create image{x,y,z,c} filled `val`.
## Parameters
* x,y,z,c - image's x-size, y-size, z-size and spectrum.
* val - filling value.
## Examples
```Elixir
iex> img = CImg.create(200, 100, 1, 3, 127)
```
"""
def create(x, y, z, c, val) when is_integer(val) do
with {:ok, h} <- NIF.cimg_create(x, y, z, c, val),
do: %CImg{handle: h}
end
@doc """
Create image{x,y,z,c} from raw binary.
`create_from_bin` helps you to make the image from the serialiezed output tensor of DNN model.
## Parameters
* bin - raw binary data to have in a image.
* x,y,z,c - image's x-size, y-size, z-size and spectrum.
* dtype - data type in the binary. any data types are converted to int8 in the image.
- "<f4" - 32bit float (available value in range 0.0..1.0)
- "<u1" - 8bit unsigned integer
## Examples
```Elixir
iex> bin = TflInterp.get_output_tensor(__MODULE__, 0)
iex> img = CImg.create_from_bin(bin, 300, 300, 1, 3, "<f4")
```
"""
def create_from_bin(bin, x, y, z, c, dtype) when is_binary(bin) do
with {:ok, h} <- NIF.cimg_from_bin(bin, x, y, z, c, dtype),
do: %CImg{handle: h}
end
@doc """
Load a image from file.
## Parameters
* fname - file path of the image. (only jpeg images - xxx.jpg - are available now)
## Examples
```Elixir
iex> img = CImg.load("sample.jpg")
```
"""
def load(fname) do
with {:ok, h} <- NIF.cimg_load(fname),
do: %CImg{handle: h}
end
@doc """
Load a image from memory.
You can create an image from loaded binary data of the image file.
## Parameters
* bin - loaded binary of the image file.
## Examples
```Elixir
iex> bin = File.read!("sample.jpg")
iex> img = CImg.load_from_memory(bin)
```
"""
def load_from_memory(bin) do
with {:ok, h} <- NIF.cimg_load_from_memory(bin),
do: %CImg{handle: h}
end
@doc """
Duplicate the image.
## Parameters
* cimg - image object %CImg{} to duplicate.
## Examples
```
iex> img = CImg.dup(original)
# create new image object `img` have same shape and values of original.
```
"""
def dup(cimg) do
with {:ok, h} <- NIF.cimg_dup(cimg),
do: %CImg{handle: h}
end
@doc """
Save the image to the file.
## Parameters
* cimg - image object %CImg{} to save.
* fname - file path for the image. (only jpeg images - xxx.jpg - are available now)
## Examples
```Elixir
iex> CImg.save(img, "sample.jpg")
```
"""
defdelegate save(cimg, fname),
to: NIF, as: :cimg_save
@doc """
Get a new image object resized {x, y}.
## Parameters
* cimg - image object %CImg{} to save.
* {x, y} - resize width and height
* align - alignment mode
- :none - fit resizing
- :ul - fixed aspect resizing, upper-leftt alignment.
- :br - fixed aspect resizing, bottom-right alignment.
* fill - filling value for the margins, when fixed aspect resizing.
## Examples
```Elixir
iex> img = CImg.load("sample.jpg")
iex> res = CImg.get_resize(img, {300,300}, :ul)
```
"""
def get_resize(cimg, {x, y}, align \\ :none, fill \\ 0) do
align = case align do
:none -> 0
:ul -> 1
:br -> 2
_ -> raise(ArgumentError, "unknown align '#{align}'.")
end
with {:ok, packed} <- NIF.cimg_get_resize(cimg, x, y, align, fill),
do: %CImg{handle: packed}
end
defdelegate blur(cimg, sigma, boundary_conditions \\ true, is_gaussian \\ true),
to: NIF, as: :cimg_blur
@doc """
[mut] mirroring the image on `axis`
## Parameters
* cimg - image object %CImg{} to save.
* axis - flipping axis: :x, :y
## Examples
```Elixir
iex> mirror = CImg.mirror(img, :y)
# vertical flipping
```
"""
def mirror(cimg, axis) when axis in [:x, :y] do
NIF.cimg_mirror(cimg, axis)
end
@doc """
Get the gray image of the image.
## Parameters
* cimg - image object %CImg{} to save.
* opt_pn - intensity inversion: 0 (default) - no-inversion, 1 - inversion
## Examples
```Elixir
iex> gray = CImg.get_gray(img, 1)
# get inverted gray image
```
"""
def get_gray(cimg, opt_pn \\ 0) do
with {:ok, gray} <- NIF.cimg_get_gray(cimg, opt_pn),
do: %CImg{handle: gray}
end
def get_crop(cimg, x0, y0, z0, c0, x1, y1, z1, c1, boundary_conditions \\ 0) do
with {:ok, crop} <- NIF.cimg_get_crop(cimg, x0, y0, z0, c0, x1, y1, z1, c1, boundary_conditions),
do: %CImg{handle: crop}
end
@doc """
Get serialized binary of the image from top-left to bottom-right.
`to_flat/2` helps you to make 32bit-float arrary for the input tensors of DNN model.
## Parameters
* cimg - image object.
* opts - conversion options
- { :dtype, xx } - convert pixel value to data type.
available: "<f4"/32bit-float, "<u1"/8bit-unsigned-char
- { :range, {lo, hi} } - normarilzed range when :dtype is "<f4".
default range: {0.0, 1.0}
- :nchw - transform axes NHWC to NCHW.
- :bgr - convert color RGB -> BGR.
## Examples
```Elixir
iex> img = CImg.load("sample.jpg")
iex> bin1 = CImg.to_flat(img, [{dtype: "<f4"}, {:range, {-1.0, 1.0}}, :nchw])
# convert pixel value to 32bit-float in range -1.0..1.0 and transform axis to NCHW.
iex> bin2 = CImg.to_flat(img, dtype: "<f4")
# convert pixel value to 32bit-float in range 0.0..1.0.
```
"""
def to_flat(cimg, opts \\ []) do
dtype = Keyword.get(opts, :dtype, "<f4")
{lo, hi} = Keyword.get(opts, :range, {0.0, 1.0})
nchw = :nchw in opts
bgr = :bgr in opts
with {:ok, bin} <- NIF.cimg_to_bin(cimg, dtype, lo, hi, nchw, bgr) do
%{descr: dtype, fortran_order: false, shape: {size(cimg)}, data: bin}
end
end
def map(cimg, lut, boundary \\ 0) do
with {:ok, h} <- NIF.cimg_map(cimg, lut, boundary),
do: %CImg{handle: h}
end
@doc """
[mut] Draw rectangle in the image.
## Parameters
* cimg - image object.
* x0,y0,x1,y1 - diagonal coordinates. if all of them are integer, they mean
actual coodinates. if all of them are float within 0.0-1.0, they mean ratio
of the image.
* color - boundary color
* opacity - opacity: 0.0-1.0
* pattern - boundary line pattern: 32bit pattern
## Examples
```Elixir
iex> CImg.draw_rect(img, 50, 30, 100, 80, {255, 0, 0}, 0.3, 0xFF00FF00)
iex> CImg.draw_rect(img, 0.2, 0.3, 0.6, 0.8, {0, 255, 0})
```
"""
def draw_rect(cimg, x0, y0, x1, y1, color, opacity \\ 1.0, pattern \\ 0xFFFFFFFF) do
cond do
Enum.all?([x0, y0, x1, y1], &is_integer/1) ->
NIF.cimg_draw_rect(cimg, x0, y0, x1, y1, color, opacity, pattern)
Enum.all?([x0, y0, x1, y1], fn x -> 0.0 <= x and x <= 1.0 end) ->
NIF.cimg_draw_ratio_rect(cimg, x0, y0, x1, y1, color, opacity, pattern)
end
end
@doc """
[mut] Filling the image with `val`.
## Parameters
* cimg - image object.
* val - filling value.
## Examples
```Elixir
iex> res = CImg.fill(img, 0x7f)
```
"""
defdelegate fill(cimg, val),
to: NIF, as: :cimg_fill
defdelegate draw_graph(cimg, data, color, opacity \\ 1.0, plot_type \\ 1, vertex_type \\ 1, ymin \\ 0.0, ymax \\ 0.0, pattern \\ 0xFFFFFFFF),
to: NIF, as: :cimg_draw_graph
@doc """
[mut] Set the pixel value at (x, y).
## Parameters
* cimg - image object.
* val - value.
* x,y,z,c - location in the image.
## Examples
```Elixir
iex> res = CImg.set(0x7f, 120, 40)
```
"""
defdelegate set(val, cimg, x, y \\ 0, z \\ 0, c \\ 0),
to: NIF, as: :cimg_set
@doc """
Get the pixel value at (x, y).
## Parameters
* cimg - image object.
* x,y,z,c - location in the image.
## Examples
```Elixir
iex> x = CImg.get(120, 40)
```
"""
defdelegate get(cimg, x, y \\ 0, z \\ 0, c \\ 0),
to: NIF, as: :cimg_get
@doc """
[mut] Assign `cimg_src` to `cimg`.
## Parameters
* cimg - image object.
* cimg_src - source image to assign
## Examples
```Elixir
iex> res = CImg.assign(imge, image_src)
```
"""
defdelegate assign(cimg, cimg_src),
to: NIF, as: :cimg_assign
@doc """
[mut] Draw filled circle in the image.
## Parameters
* cimg - image object.
* x0,y0 - circle center location
* radius - circle radius
* color - filling color
* opacity - opacity: 0.0-1.0
## Examples
```Elixir
iex> res = CImg.draw_circle(imge, 100, 80, 40, {0, 0, 255})
```
"""
defdelegate draw_circle(cimg, x0, y0, radius, color, opacity \\ 1.0),
to: NIF, as: :cimg_draw_circle
@doc """
[mut] Draw circle in the image.
## Parameters
* cimg - image object.
* x0,y0 - circle center location
* radius - circle radius
* color - boundary color
* opacity - opacity: 0.0-1.0
* pattern - boundary line pattern
## Examples
```Elixir
iex> res = CImg.draw_circle(imge, 100, 80, 40, {0, 0, 255}, 0.3, 0xFFFFFFFF)
```
"""
defdelegate draw_circle(cimg, x0, y0, radius, color, opacity, pattern),
to: NIF, as: :cimg_draw_circle
@doc """
Get shape {x,y,z,c} of the image
## Parameters
* cimg - image object.
## Examples
```Elixir
iex> shape = CImg.shape(imge)
```
"""
defdelegate shape(cimg),
to: NIF, as: :cimg_shape
@doc """
Get byte size of the image
## Parameters
* cimg - image object.
## Examples
```Elixir
iex> size = CImg.sizh(imge)
```
"""
defdelegate size(cimg),
to: NIF, as: :cimg_size
defdelegate transfer(cimg, cimg_src, mapping, cx \\ 0, cy \\ 0, cz \\ 0),
to: NIF, as: :cimg_transfer
@doc """
[mut] Thresholding the image.
## Parameters
* cimg - image object.
* val - threshold value
* soft -
* strict -
## Examples
```Elixir
iex> res = CImg.threshold(imge, 100)
```
"""
defdelegate threshold(cimg, val, soft \\ false, strict \\ false),
to: NIF, as: :cimg_threshold
@doc """
Display the image on the CImgDisplay object.
## Parameters
* cimg - image object.
* display - CImgDisplay object
## Examples
```Elixir
iex> disp = CImgDisplay.create(img, "Sample")
iex> CImg.display(imge, disp)
```
"""
defdelegate display(cimg, disp),
to: NIF, as: :cimg_display
end