我正在尝试在Elixir中创建一个基本图像(位图)写入器,但我坚持了一点。
我尝试创建一个将像素设置为二进制的函数。我使用模式匹配,但我的功能显然太慢(超过10分钟将所有像素设置为1024 * 768的图片)。
目前,我的二进制文件大小等于宽度*高度。就像你在下面的代码中看到的那样,我的函数将x和y作为参数,并且必须在这个位置修改一个int。
# Function
def replace_by_test(output, width, x, y) do
out_offset = y * width + x
<<
o_before :: binary-size(out_offset),
_ :: binary-size(4),
o_after :: binary
>> = output
<< o_before :: binary, "TEST" :: binary, o_after :: binary >>
end
# Test on a 1024 * 768 resolution image
out_size = 1024 * 768 * 8
output = << 0 :: size(out_size) >>
for x <- 0..(1024*768-1), do: replace_by_test(output, 1024, 0, 0)
让这段代码更快。如果可能,请在不到10秒的时间内运行。
答案 0 :(得分:5)
构造一个新的二进制文件会导致Erlang必须在每次迭代时复制整个二进制文件。复制786432个字节786432次必然会很慢(需要分配的内存为618GB,然后很快就会自由发送!)。您需要为此使用不同的数据结构。
一种选择是在构造二进制文件时使用Map,然后在完成修改后将其转换为二进制文件:
# Create a blank image.
map = for x <- 1..1024, y <- 1..768, into: %{}, do: {{x, y}, 0}
# Set each pixel once to x * y
map =
0..1023
|> Enum.reduce(map, fn x, map ->
0..767 |> Enum.reduce(map, fn y, map ->
Map.put(map, {x, y}, 0)
end)
end)
# Get back a binary
binary = for x <- 0..1023, y <- 0..767, into: <<>>, do: <<Map.get(map, {x, y})>>
IO.inspect binary
IO.inspect byte_size(binary)
输出:
<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
786432
整个脚本大约需要2.5秒才能在我的机器上执行。
另一种选择是使用Erlang's array
module。它可能比地图更快。在阅读该模块的文档后,应该很容易实现。
修改:此处转换为array
的代码。它在我的机器上几乎只运行1秒,比地图快2.5倍。
# Create a blank image.
array = :array.new(size: 1024 * 768, default: 0)
# Set each pixel once to x * y
array =
0..1023
|> Enum.reduce(array, fn x, array ->
0..767 |> Enum.reduce(array, fn y, array ->
:array.set(y * 1024 + x, 123, array)
end)
end)
# Get back a binary
binary = :array.foldl(fn _, x, acc -> <<acc::binary, x>> end, <<>>, array)