Skip to content

Instantly share code, notes, and snippets.

@varsill
Created December 13, 2024 07:56
Show Gist options
  • Save varsill/ea729390b44ffb256a0dbd91bfb146e1 to your computer and use it in GitHub Desktop.
Save varsill/ea729390b44ffb256a0dbd91bfb146e1 to your computer and use it in GitHub Desktop.
Mix.install([:vix, :image])
alias Vix.Vips.Operation
alias Vix.Vips.Image, as: Vimage
how_many_videos = 10
how_many_frames = 200
defmodule Helpers do
def compose(files, frame_num) do
image_id = rem(frame_num, Map.keys(files) |> length())
{:ok, {overlay, _flags}} = Operation.jpegload_buffer(files[image_id])
{width, height} = {Image.width(overlay), Image.height(overlay)}
{:ok, overlay_yuv} = Image.YUV.write_to_binary(overlay, :C420)
{overlay_y, overlay_u, overlay_v} =
open_planes(overlay_yuv, width, height) |> add_alpha(width, height)
{:ok, underlay_yuv} =
Operation.black!(2 * width, 2 * height, bands: 3) |> Image.YUV.write_to_binary(:C420)
{underlay_y, underlay_u, underlay_v} = open_planes(underlay_yuv, 2 * width, 2 * height)
{x, y} = {Enum.random(1..width), Enum.random(1..height)}
composed_y = compose_plane(underlay_y, overlay_y, x, y)
composed_u = compose_plane(underlay_u, overlay_u, div(x, 2), div(y, 2))
composed_v = compose_plane(underlay_v, overlay_v, div(x, 2), div(y, 2))
_res = composed_y <> composed_u <> composed_v
end
defp open_planes(payload, width, height) do
y_size = width * height
half_width = div(width, 2)
half_height = div(height, 2)
uv_size = half_width * half_height
<<y::binary-size(y_size), u::binary-size(uv_size), v::binary-size(uv_size)>> = payload
{:ok, y} = Vimage.new_from_binary(y, width, height, 1, :VIPS_FORMAT_UCHAR)
{:ok, u} = Vimage.new_from_binary(u, half_width, half_height, 1, :VIPS_FORMAT_UCHAR)
{:ok, v} = Vimage.new_from_binary(v, half_width, half_height, 1, :VIPS_FORMAT_UCHAR)
{y, u, v}
end
defp compose_plane(underlay_channel, overlay_channel, x, y) do
{:ok, composed} =
Operation.composite2(
underlay_channel,
overlay_channel,
:VIPS_BLEND_MODE_OVER,
[x: x, y: y, "compositing-space": :VIPS_INTERPRETATION_B_W]
)
{:ok, plane} = Vimage.write_to_binary(composed[0])
plane
end
defp add_alpha(planes, width, height) do
luminance_alpha = mask(width, height)
half_width = div(width, 2)
half_height = div(height, 2)
chroma_alpha = Image.new!(half_width, half_height, color: 128)
{y, u, v} = planes
y =
Operation.bandjoin!([y, luminance_alpha[0]])
|> Operation.linear!([1.0], [0.0], uchar: true)
u =
Operation.bandjoin!([u, chroma_alpha[0]]) |> Operation.linear!([1.0], [0.0], uchar: true)
v =
Operation.bandjoin!([v, chroma_alpha[0]]) |> Operation.linear!([1.0], [0.0], uchar: true)
{y, u, v}
end
defp mask(width, height) do
use Image.Math
xyz = Operation.xyz!(width, height) - [width / 2, height / 2]
(xyz[0] ** 2 + xyz[1] ** 2) ** 0.5
end
end
inputs_dir = "./inputs/"
files =
File.ls!(inputs_dir)
|> Enum.with_index()
|> Enum.map(fn {file_name, id} ->
{id, Path.join(inputs_dir, file_name) |> File.read!()}
end)
|> Map.new()
:erlang.system_flag(:microstate_accounting, true)
:observer.start()
pids =
Enum.map(1..how_many_videos, fn _video_id ->
Task.async(fn ->
Enum.each(1..how_many_frames, &Helpers.compose(files, &1))
end)
end)
Enum.each(pids, &Task.await(&1, :infinity))
schedulers_times =
:erlang.statistics(:microstate_accounting)
|> Enum.filter(fn entry -> entry.type == :scheduler end)
|> Enum.map(fn entry ->
counters =
Enum.map(entry.counters, fn {key, time} -> {key, time / 1_000_000_000} end) |> Map.new()
%{entry | counters: counters}
end)
|> Enum.sort(fn a, b -> a.id < b.id end)
dbg(schedulers_times)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment