Current section

Files

Jump to
viva_tensor src viva_tensor@vision@transforms.erl
Raw

src/viva_tensor@vision@transforms.erl

-module(viva_tensor@vision@transforms).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/vision/transforms.gleam").
-export([resize/4, center_crop/3, random_crop/3, horizontal_flip/1, vertical_flip/1, random_horizontal_flip/2, normalize/3, to_grayscale/2, adjust_brightness/2, adjust_contrast/2, to_tensor/4, to_byte_image/1, compose/2]).
-export_type([resize_mode/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-type resize_mode() :: nearest | bilinear.
-file("src/viva_tensor/vision/transforms.gleam", 38).
?DOC(false).
-spec chw_or_bchw(binary(), list(integer())) -> {ok,
{integer(), integer(), integer(), integer(), boolean()}} |
{error, viva_tensor@core@error:tensor_error()}.
chw_or_bchw(Op, Shp) ->
case Shp of
[C, H, W] ->
{ok, {1, C, H, W, false}};
[B, C@1, H@1, W@1] ->
{ok, {B, C@1, H@1, W@1, true}};
_ ->
{error, {rank_mismatch, Op, 3, Shp}}
end.
-file("src/viva_tensor/vision/transforms.gleam", 49).
?DOC(false).
-spec make_shape(integer(), integer(), integer(), boolean(), integer()) -> list(integer()).
make_shape(C, H, W, Batched, Batch) ->
case Batched of
true ->
[Batch, C, H, W];
false ->
[C, H, W]
end.
-file("src/viva_tensor/vision/transforms.gleam", 56).
?DOC(false).
-spec min_float(float(), float()) -> float().
min_float(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/viva_tensor/vision/transforms.gleam", 63).
?DOC(false).
-spec max_float(float(), float()) -> float().
max_float(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/viva_tensor/vision/transforms.gleam", 70).
?DOC(false).
-spec clamp_f(float(), float(), float()) -> float().
clamp_f(X, Lo, Hi) ->
max_float(Lo, min_float(Hi, X)).
-file("src/viva_tensor/vision/transforms.gleam", 74).
?DOC(false).
-spec clamp_i(integer(), integer(), integer()) -> integer().
clamp_i(X, Lo, Hi) ->
case X < Lo of
true ->
Lo;
false ->
case X > Hi of
true ->
Hi;
false ->
X
end
end.
-file("src/viva_tensor/vision/transforms.gleam", 85).
?DOC(false).
-spec int_to_float(integer()) -> float().
int_to_float(I) ->
erlang:float(I).
-file("src/viva_tensor/vision/transforms.gleam", 89).
?DOC(false).
-spec list_get(list(float()), integer()) -> float().
list_get(Xs, Idx) ->
case Xs of
[] ->
+0.0;
[Head | Rest] ->
case Idx of
0 ->
Head;
_ ->
list_get(Rest, Idx - 1)
end
end.
-file("src/viva_tensor/vision/transforms.gleam", 104).
?DOC(false).
-spec sample_clamped(
list(float()),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> float().
sample_clamped(Data, B, C, H, W, Channels, Height, Width) ->
H_c = clamp_i(H, 0, Height - 1),
W_c = clamp_i(W, 0, Width - 1),
Idx = (((((B * Channels) * Height) * Width) + ((C * Height) * Width)) + (H_c
* Width))
+ W_c,
list_get(Data, Idx).
-file("src/viva_tensor/vision/transforms.gleam", 204).
?DOC(false).
-spec sample_bilinear(
list(float()),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> float().
sample_bilinear(Data, B, Ch, Oh, Ow, Channels, In_h, In_w, Out_h, Out_w) ->
Src_h = (case int_to_float(Out_h) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (int_to_float(Oh) + 0.5) * int_to_float(In_h) / Gleam@denominator
end) - 0.5,
Src_w = (case int_to_float(Out_w) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> (int_to_float(Ow) + 0.5) * int_to_float(In_w) / Gleam@denominator@1
end) - 0.5,
H_clamped = clamp_f(Src_h, +0.0, int_to_float(In_h - 1)),
W_clamped = clamp_f(Src_w, +0.0, int_to_float(In_w - 1)),
H0 = erlang:trunc(H_clamped),
W0 = erlang:trunc(W_clamped),
H1 = clamp_i(H0 + 1, 0, In_h - 1),
W1 = clamp_i(W0 + 1, 0, In_w - 1),
Dh = H_clamped - int_to_float(H0),
Dw = W_clamped - int_to_float(W0),
V00 = sample_clamped(Data, B, Ch, H0, W0, Channels, In_h, In_w),
V01 = sample_clamped(Data, B, Ch, H0, W1, Channels, In_h, In_w),
V10 = sample_clamped(Data, B, Ch, H1, W0, Channels, In_h, In_w),
V11 = sample_clamped(Data, B, Ch, H1, W1, Channels, In_h, In_w),
Top = (V00 * (1.0 - Dw)) + (V01 * Dw),
Bot = (V10 * (1.0 - Dw)) + (V11 * Dw),
(Top * (1.0 - Dh)) + (Bot * Dh).
-file("src/viva_tensor/vision/transforms.gleam", 176).
?DOC(false).
-spec sample_nearest(
list(float()),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> float().
sample_nearest(Data, B, Ch, Oh, Ow, Channels, In_h, In_w, Out_h, Out_w) ->
Src_h = (case int_to_float(Out_h) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (int_to_float(Oh) + 0.5) * int_to_float(In_h) / Gleam@denominator
end) - 0.5,
Src_w = (case int_to_float(Out_w) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> (int_to_float(Ow) + 0.5) * int_to_float(In_w) / Gleam@denominator@1
end) - 0.5,
Ih = erlang:round(Src_h),
Iw = erlang:round(Src_w),
sample_clamped(Data, B, Ch, Ih, Iw, Channels, In_h, In_w).
-file("src/viva_tensor/vision/transforms.gleam", 129).
?DOC(false).
-spec resize(viva_tensor@tensor:tensor(), integer(), integer(), resize_mode()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
resize(Image, New_h, New_w, Mode) ->
gleam@result:'try'(
chw_or_bchw(<<"resize"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
case (New_h =< 0) orelse (New_w =< 0) of
true ->
{error,
{invalid_shape,
<<<<<<"resize: new_h and new_w must be positive, got "/utf8,
(erlang:integer_to_binary(New_h))/binary>>/binary,
"x"/utf8>>/binary,
(erlang:integer_to_binary(New_w))/binary>>}};
false ->
Data = viva_tensor@tensor:to_list(Image),
Out_data = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) -> _pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) ->
_pipe@2 = gleam@list:range(0, New_h - 1),
gleam@list:flat_map(
_pipe@2,
fun(Oh) ->
_pipe@3 = gleam@list:range(
0,
New_w - 1
),
gleam@list:map(
_pipe@3,
fun(Ow) -> case Mode of
nearest ->
sample_nearest(
Data,
B,
Ch,
Oh,
Ow,
C,
H,
W,
New_h,
New_w
);
bilinear ->
sample_bilinear(
Data,
B,
Ch,
Oh,
Ow,
C,
H,
W,
New_h,
New_w
)
end end
)
end
)
end
) end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out_data),
make_shape(C, New_h, New_w, Batched, Batch)
)
end
end
).
-file("src/viva_tensor/vision/transforms.gleam", 304).
?DOC(false).
-spec crop_region(
viva_tensor@tensor:tensor(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
boolean()
) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
crop_region(Image, Batch, C, H, W, Top, Left, Target_h, Target_w, Batched) ->
Data = viva_tensor@tensor:to_list(Image),
Out = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) -> _pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) -> _pipe@2 = gleam@list:range(0, Target_h - 1),
gleam@list:flat_map(
_pipe@2,
fun(Oh) ->
_pipe@3 = gleam@list:range(0, Target_w - 1),
gleam@list:map(
_pipe@3,
fun(Ow) ->
sample_clamped(
Data,
B,
Ch,
Top + Oh,
Left + Ow,
C,
H,
W
)
end
)
end
) end
) end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(C, Target_h, Target_w, Batched, Batch)
).
-file("src/viva_tensor/vision/transforms.gleam", 252).
?DOC(false).
-spec center_crop(viva_tensor@tensor:tensor(), integer(), integer()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
center_crop(Image, Target_h, Target_w) ->
gleam@result:'try'(
chw_or_bchw(<<"center_crop"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
case (((Target_h > H) orelse (Target_w > W)) orelse (Target_h =< 0))
orelse (Target_w =< 0) of
true ->
{error,
{invalid_shape,
<<"center_crop: target size exceeds image"/utf8>>}};
false ->
Top = (H - Target_h) div 2,
Left = (W - Target_w) div 2,
crop_region(
Image,
Batch,
C,
H,
W,
Top,
Left,
Target_h,
Target_w,
Batched
)
end
end
).
-file("src/viva_tensor/vision/transforms.gleam", 277).
?DOC(false).
-spec random_crop(viva_tensor@tensor:tensor(), integer(), integer()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
random_crop(Image, Target_h, Target_w) ->
gleam@result:'try'(
chw_or_bchw(<<"random_crop"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
case (((Target_h > H) orelse (Target_w > W)) orelse (Target_h =< 0))
orelse (Target_w =< 0) of
true ->
{error,
{invalid_shape,
<<"random_crop: target size exceeds image"/utf8>>}};
false ->
Max_top = H - Target_h,
Max_left = W - Target_w,
Top = case Max_top of
0 ->
0;
N ->
gleam@int:random(N + 1)
end,
Left = case Max_left of
0 ->
0;
N@1 ->
gleam@int:random(N@1 + 1)
end,
crop_region(
Image,
Batch,
C,
H,
W,
Top,
Left,
Target_h,
Target_w,
Batched
)
end
end
).
-file("src/viva_tensor/vision/transforms.gleam", 342).
?DOC(false).
-spec horizontal_flip(viva_tensor@tensor:tensor()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
horizontal_flip(Image) ->
gleam@result:'try'(
chw_or_bchw(<<"horizontal_flip"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
Data = viva_tensor@tensor:to_list(Image),
Out = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) -> _pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) -> _pipe@2 = gleam@list:range(0, H - 1),
gleam@list:flat_map(
_pipe@2,
fun(Row) ->
_pipe@3 = gleam@list:range(0, W - 1),
gleam@list:map(
_pipe@3,
fun(Col) ->
sample_clamped(
Data,
B,
Ch,
Row,
(W - 1) - Col,
C,
H,
W
)
end
)
end
) end
) end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(C, H, W, Batched, Batch)
)
end
).
-file("src/viva_tensor/vision/transforms.gleam", 370).
?DOC(false).
-spec vertical_flip(viva_tensor@tensor:tensor()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
vertical_flip(Image) ->
gleam@result:'try'(
chw_or_bchw(<<"vertical_flip"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
Data = viva_tensor@tensor:to_list(Image),
Out = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) -> _pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) -> _pipe@2 = gleam@list:range(0, H - 1),
gleam@list:flat_map(
_pipe@2,
fun(Row) ->
_pipe@3 = gleam@list:range(0, W - 1),
gleam@list:map(
_pipe@3,
fun(Col) ->
sample_clamped(
Data,
B,
Ch,
(H - 1) - Row,
Col,
C,
H,
W
)
end
)
end
) end
) end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(C, H, W, Batched, Batch)
)
end
).
-file("src/viva_tensor/vision/transforms.gleam", 398).
?DOC(false).
-spec random_horizontal_flip(viva_tensor@tensor:tensor(), float()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
random_horizontal_flip(Image, P) ->
P_clamped = clamp_f(P, +0.0, 1.0),
Resolution = 1000000,
Threshold = erlang:round(P_clamped * int_to_float(Resolution)),
case gleam@int:random(Resolution) < Threshold of
true ->
horizontal_flip(Image);
false ->
gleam@result:'try'(
chw_or_bchw(
<<"random_horizontal_flip"/utf8>>,
viva_tensor@tensor:shape(Image)
),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(
viva_tensor@tensor:to_list(Image)
),
make_shape(C, H, W, Batched, Batch)
)
end
)
end.
-file("src/viva_tensor/vision/transforms.gleam", 430).
?DOC(false).
-spec normalize(viva_tensor@tensor:tensor(), list(float()), list(float())) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
normalize(Image, Mean, Std) ->
gleam@result:'try'(
chw_or_bchw(<<"normalize"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
case (erlang:length(Mean) =:= C) andalso (erlang:length(Std) =:= C) of
false ->
{error,
{invalid_shape,
<<<<"normalize: mean/std length must equal channels ("/utf8,
(erlang:integer_to_binary(C))/binary>>/binary,
")"/utf8>>}};
true ->
case gleam@list:any(Std, fun(S) -> S =:= +0.0 end) of
true ->
{error,
{invalid_shape,
<<"normalize: std entries must be non-zero"/utf8>>}};
false ->
Data = viva_tensor@tensor:to_list(Image),
Out = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) ->
_pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) ->
M = list_get(Mean, Ch),
S@1 = list_get(Std, Ch),
_pipe@2 = gleam@list:range(
0,
H - 1
),
gleam@list:flat_map(
_pipe@2,
fun(Row) ->
_pipe@3 = gleam@list:range(
0,
W - 1
),
gleam@list:map(
_pipe@3,
fun(Col) ->
V = sample_clamped(
Data,
B,
Ch,
Row,
Col,
C,
H,
W
),
case S@1 of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (V
- M)
/ Gleam@denominator
end
end
)
end
)
end
)
end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(C, H, W, Batched, Batch)
)
end
end
end
).
-file("src/viva_tensor/vision/transforms.gleam", 554).
?DOC(false).
-spec slice_list(list(float()), integer(), integer()) -> list(float()).
slice_list(Xs, Offset, Length) ->
_pipe = Xs,
_pipe@1 = gleam@list:drop(_pipe, Offset),
gleam@list:take(_pipe@1, Length).
-file("src/viva_tensor/vision/transforms.gleam", 490).
?DOC(false).
-spec to_grayscale(viva_tensor@tensor:tensor(), integer()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
to_grayscale(Image, Num_output_channels) ->
gleam@result:'try'(
chw_or_bchw(<<"to_grayscale"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
case C =:= 3 of
false ->
{error,
{invalid_shape,
<<"to_grayscale: input must have 3 channels, got "/utf8,
(erlang:integer_to_binary(C))/binary>>}};
true ->
case (Num_output_channels =:= 1) orelse (Num_output_channels
=:= 3) of
false ->
{error,
{invalid_shape,
<<"to_grayscale: num_output_channels must be 1 or 3, got "/utf8,
(erlang:integer_to_binary(
Num_output_channels
))/binary>>}};
true ->
Data = viva_tensor@tensor:to_list(Image),
Luma_data = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) ->
_pipe@1 = gleam@list:range(0, H - 1),
gleam@list:flat_map(
_pipe@1,
fun(Row) ->
_pipe@2 = gleam@list:range(
0,
W - 1
),
gleam@list:map(
_pipe@2,
fun(Col) ->
R = sample_clamped(
Data,
B,
0,
Row,
Col,
3,
H,
W
),
G = sample_clamped(
Data,
B,
1,
Row,
Col,
3,
H,
W
),
Bl = sample_clamped(
Data,
B,
2,
Row,
Col,
3,
H,
W
),
((0.299 * R) + (0.587 * G))
+ (0.114 * Bl)
end
)
end
)
end
)
end,
case Num_output_channels of
1 ->
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Luma_data),
make_shape(1, H, W, Batched, Batch)
);
_ ->
Plane_size = H * W,
Out = begin
_pipe@3 = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe@3,
fun(B@1) ->
Plane = slice_list(
Luma_data,
B@1 * Plane_size,
Plane_size
),
lists:append(
[Plane, Plane, Plane]
)
end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(3, H, W, Batched, Batch)
)
end
end
end
end
).
-file("src/viva_tensor/vision/transforms.gleam", 565).
?DOC(false).
-spec adjust_brightness(viva_tensor@tensor:tensor(), float()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
adjust_brightness(Image, Factor) ->
gleam@result:'try'(
chw_or_bchw(
<<"adjust_brightness"/utf8>>,
viva_tensor@tensor:shape(Image)
),
fun(_use0) ->
{_, _, _, _, _} = _use0,
Data = viva_tensor@tensor:to_list(Image),
Out = gleam@list:map(
Data,
fun(V) -> clamp_f(V * Factor, +0.0, 1.0) end
),
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
viva_tensor@tensor:shape(Image)
)
end
).
-file("src/viva_tensor/vision/transforms.gleam", 585).
?DOC(false).
-spec adjust_contrast(viva_tensor@tensor:tensor(), float()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
adjust_contrast(Image, Factor) ->
gleam@result:'try'(
chw_or_bchw(<<"adjust_contrast"/utf8>>, viva_tensor@tensor:shape(Image)),
fun(_use0) ->
{Batch, C, H, W, Batched} = _use0,
Data = viva_tensor@tensor:to_list(Image),
N = H * W,
N_f = int_to_float(N),
Out = begin
_pipe = gleam@list:range(0, Batch - 1),
gleam@list:flat_map(
_pipe,
fun(B) -> _pipe@1 = gleam@list:range(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) ->
Plane = begin
_pipe@2 = gleam@list:range(0, H - 1),
gleam@list:flat_map(
_pipe@2,
fun(Row) ->
_pipe@3 = gleam@list:range(0, W - 1),
gleam@list:map(
_pipe@3,
fun(Col) ->
sample_clamped(
Data,
B,
Ch,
Row,
Col,
C,
H,
W
)
end
)
end
)
end,
Sum = gleam@list:fold(
Plane,
+0.0,
fun(Acc, V) -> Acc + V end
),
Mean = case N_f of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Sum / Gleam@denominator
end,
gleam@list:map(
Plane,
fun(V@1) ->
clamp_f(
Mean + (Factor * (V@1 - Mean)),
+0.0,
1.0
)
end
)
end
) end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Out),
make_shape(C, H, W, Batched, Batch)
)
end
).
-file("src/viva_tensor/vision/transforms.gleam", 630).
?DOC(false).
-spec to_tensor(list(integer()), integer(), integer(), integer()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
to_tensor(Byte_image, Height, Width, Channels) ->
case ((Height =< 0) orelse (Width =< 0)) orelse (Channels =< 0) of
true ->
{error,
{invalid_shape,
<<<<<<<<<<<<"to_tensor: dimensions must be positive (got h="/utf8,
(erlang:integer_to_binary(Height))/binary>>/binary,
", w="/utf8>>/binary,
(erlang:integer_to_binary(Width))/binary>>/binary,
", c="/utf8>>/binary,
(erlang:integer_to_binary(Channels))/binary>>/binary,
")"/utf8>>}};
false ->
Expected = (Height * Width) * Channels,
case erlang:length(Byte_image) =:= Expected of
false ->
{error,
{invalid_shape,
<<<<<<"to_tensor: byte_image length "/utf8,
(erlang:integer_to_binary(
erlang:length(Byte_image)
))/binary>>/binary,
" != h*w*c = "/utf8>>/binary,
(erlang:integer_to_binary(Expected))/binary>>}};
true ->
Hwc = gleam@list:map(Byte_image, fun erlang:float/1),
Chw = begin
_pipe = gleam@list:range(0, Channels - 1),
gleam@list:flat_map(
_pipe,
fun(Ch) ->
_pipe@1 = gleam@list:range(0, Height - 1),
gleam@list:flat_map(
_pipe@1,
fun(Row) ->
_pipe@2 = gleam@list:range(0, Width - 1),
gleam@list:map(
_pipe@2,
fun(Col) ->
Idx = (((Row * Width) + Col) * Channels)
+ Ch,
list_get(Hwc, Idx) / 255.0
end
)
end
)
end
)
end,
viva_tensor@tensor:reshape(
viva_tensor@tensor:from_list(Chw),
[Channels, Height, Width]
)
end
end.
-file("src/viva_tensor/vision/transforms.gleam", 689).
?DOC(false).
-spec to_byte_image(viva_tensor@tensor:tensor()) -> {ok, list(integer())} |
{error, viva_tensor@core@error:tensor_error()}.
to_byte_image(Image) ->
Shp = viva_tensor@tensor:shape(Image),
case Shp of
[C, H, W] ->
Data = viva_tensor@tensor:to_list(Image),
Bytes = begin
_pipe = gleam@list:range(0, H - 1),
gleam@list:flat_map(
_pipe,
fun(Row) -> _pipe@1 = gleam@list:range(0, W - 1),
gleam@list:flat_map(
_pipe@1,
fun(Col) -> _pipe@2 = gleam@list:range(0, C - 1),
gleam@list:map(
_pipe@2,
fun(Ch) ->
Idx = (((Ch * H) * W) + (Row * W)) + Col,
Scaled = list_get(Data, Idx) * 255.0,
Rounded = erlang:round(Scaled),
clamp_i(Rounded, 0, 255)
end
) end
) end
)
end,
{ok, Bytes};
_ ->
{error, {rank_mismatch, <<"to_byte_image"/utf8>>, 3, Shp}}
end.
-file("src/viva_tensor/vision/transforms.gleam", 725).
?DOC(false).
-spec compose(
list(fun((viva_tensor@tensor:tensor()) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()})),
viva_tensor@tensor:tensor()
) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
compose(Transforms, Image) ->
gleam@list:try_fold(Transforms, Image, fun(Acc, F) -> F(Acc) end).