Packages

ExCubecl is a GPU compute runtime for Elixir powered by CubeCL via Rust NIFs. Provides GPU buffer management, kernel execution, async command submission, and pipeline orchestration. Currently includes CPU fallback implementations.

Current section

Files

Jump to
ex_cubecl examples image_processing.exs
Raw

examples/image_processing.exs

# Image Processing Example
# Demonstrates convolution, padding, and shape operations on image-like data
# Run with: mix run examples/image_processing.exs
alias ExCubecl.Backend, as: B
IO.puts("=== Image Processing Operations ===")
IO.puts("")
# Create a small 5x5 "image" (single channel)
image = Nx.tensor([
[10, 20, 30, 40, 50],
[60, 70, 80, 90, 100],
[110, 120, 130, 140, 150],
[160, 170, 180, 190, 200],
[210, 220, 230, 240, 250]
], backend: B)
IO.puts("Original image (5×5):")
IO.puts(" Shape: #{inspect(Nx.shape(image))}")
IO.puts(" Values: #{inspect(Nx.to_flat_list(image))}")
IO.puts("")
# Add batch dimension: {1, 5, 5}
batched = Nx.reshape(image, {1, 5, 5})
IO.puts("Batched shape: #{inspect(Nx.shape(batched))}")
IO.puts("")
# --- Padding ---
IO.puts("--- Padding ---")
# Pad with 1 pixel on each side
padded = Nx.pad(image, 0.0, [{1, 1, 0}, {1, 1, 0}])
IO.puts("Padded shape: #{inspect(Nx.shape(padded))}")
IO.puts("Padded values:")
Nx.to_flat_list(padded)
|> Enum.chunk_every(7)
|> Enum.each(fn row ->
IO.puts(" #{inspect(row)}")
end)
IO.puts("")
# --- Edge Detection Kernel ---
IO.puts("--- Edge Detection (Sobel-like) ---")
# 3x3 kernel for horizontal edge detection
kernel = Nx.tensor([
[-1.0, 0.0, 1.0],
[-2.0, 0.0, 2.0],
[-1.0, 0.0, 1.0]
], backend: B)
IO.puts("Kernel shape: #{inspect(Nx.shape(kernel))}")
# Apply convolution
batched_padded = Nx.reshape(padded, {1, 7, 7})
result = Nx.conv(batched_padded, kernel)
IO.puts("Conv result shape: #{inspect(Nx.shape(result))}")
IO.puts("")
# --- Blur Kernel ---
IO.puts("--- Box Blur ---")
blur_kernel = Nx.tensor([
[1.0, 1.0, 1.0],
[1.0, 1.0, 1.0],
[1.0, 1.0, 1.0]
], backend: B)
blur_result = Nx.conv(batched_padded, blur_kernel)
IO.puts("Blur result shape: #{inspect(Nx.shape(blur_result))}")
IO.puts("")
# --- Window Operations (Pooling) ---
IO.puts("--- Max Pooling (2×2) ---")
small = Nx.tensor([
[1.0, 3.0, 2.0, 4.0],
[5.0, 6.0, 1.0, 2.0],
[3.0, 2.0, 7.0, 8.0],
[1.0, 0.0, 4.0, 3.0]
], backend: B)
IO.puts("Input (4×4):")
Nx.to_flat_list(small)
|> Enum.chunk_every(4)
|> Enum.each(fn row -> IO.puts(" #{inspect(row)}") end)
pooled = Nx.window_max(small, [2, 2], [])
IO.puts("Max pooled (2×2):")
Nx.to_flat_list(pooled)
|> Enum.chunk_every(2)
|> Enum.each(fn row -> IO.puts(" #{inspect(row)}") end)
IO.puts("")
# --- Average via Window Sum ---
IO.puts("--- Average Pooling (2×2) ---")
sum_pooled = Nx.window_sum(small, [2, 2], [])
# Divide by 4 to get average
avg_pooled = Nx.divide(sum_pooled, Nx.tensor(4.0))
IO.puts("Average pooled (2×2):")
Nx.to_flat_list(avg_pooled)
|> Enum.chunk_every(2)
|> Enum.each(fn row -> IO.puts(" #{inspect(row)}") end)
IO.puts("")
# --- Slice (Crop) ---
IO.puts("--- Image Cropping ---")
cropped = Nx.slice(image, [1, 1], [3, 3], [1, 1])
IO.puts("Cropped (3×3 from center):")
Nx.to_flat_list(cropped)
|> Enum.chunk_every(3)
|> Enum.each(fn row -> IO.puts(" #{inspect(row)}") end)
IO.puts("")
# --- Strided Slice (Downsample) ---
IO.puts("--- Strided Slice (Downsample 2x) ---")
downsampled = Nx.slice(image, [0, 0], [3, 3], [2, 2])
IO.puts("Downsampled (every other pixel):")
Nx.to_flat_list(downsampled)
|> Enum.chunk_every(3)
|> Enum.each(fn row -> IO.puts(" #{inspect(row)}") end)
IO.puts("")
IO.puts("=== Done ===")