NDVI Client
Mix.install(
[
{:nx, "~> 0.9"},
{:evision, "~> 0.2"},
{:exla, "~> 0.9"},
{:kino, "~> 0.14"},
{:flow, "~> 1.2"}
],
config: [nx: [default_backend: EXLA.Backend]]
)
Define modules
defmodule BandInfo do
defstruct gain: 0.0, offset: 0.0
end
defmodule HeaderInfo do
defstruct red: %BandInfo{},
nir: %BandInfo{},
date: nil
def get_string(info, start, value) do
info
|> String.slice(start, value)
|> String.trim()
end
def get_value(info, start, value) do
info
|> get_string(start, value)
|> String.to_float()
end
def read(hdr_file_path) do
info = File.read!(hdr_file_path)
%HeaderInfo{
# 赤色光バンド
red: %BandInfo{
gain: get_value(info, 1752, 8),
offset: get_value(info, 1760, 8)
},
# 近赤外線バンド
nir: %BandInfo{
gain: get_value(info, 1768, 8),
offset: get_value(info, 1776, 8)
},
date: get_string(info, 192, 8)
}
end
end
defmodule NDVIClient do
import Nx.Defn
def read_header(file_path_list) do
file_path_list
|> Enum.find(fn file -> Path.extname(file) == ".txt" end)
|> HeaderInfo.read()
end
def get_band_tensor(file_path_list, prefix) do
file_path_list
|> Enum.find(fn file ->
file
|> Path.basename()
|> String.starts_with?(prefix)
end)
|> Evision.imread(flags: Evision.Constant.cv_IMREAD_GRAYSCALE())
|> Evision.resize({640, 640})
|> Evision.Mat.to_nx(EXLA.Backend)
end
defn get_luminance(tensor, gain, offset) do
tensor * gain + offset
end
def calc(file_path_list) do
header_info = read_header(file_path_list)
red_tensor =
file_path_list
|> get_band_tensor("IMG-03")
|> get_luminance(header_info.red.gain, header_info.red.offset)
nir_tensor =
file_path_list
|> get_band_tensor("IMG-04")
|> get_luminance(header_info.nir.gain, header_info.nir.offset)
ndvi_img =
[red_tensor, nir_tensor]
|> Nx.stack(axis: -1)
|> then(fn input ->
Nx.Serving.batched_run(NDVIServer, input, &Nx.backend_copy/1)
end)
|> Evision.Mat.from_nx_2d()
{header_info.date, ndvi_img}
end
end
Connect to server
server_node_inputs =
["A", "B"]
|> Enum.into(%{}, fn node_id ->
{
node_id,
%{
node: Kino.Input.text("SERVER_#{node_id}_NODE_NAME"),
cookie: Kino.Input.text("SERVER_#{node_id}_COOKIE")
}
}
end)
server_node_inputs
|> Enum.map(fn {_, inputs} ->
[inputs.node, inputs.cookie]
end)
|> List.flatten()
|> Kino.Layout.grid(columns: 2)
server_node_inputs
|> Enum.map(fn {_, inputs} ->
node_name =
inputs.node
|> Kino.Input.read()
|> String.to_atom()
cookie =
inputs.cookie
|> Kino.Input.read()
|> String.to_atom()
Node.set_cookie(node_name, cookie)
Node.connect(node_name)
end)
Get NDVI
scene_id_list = [
"202ce08d-ba4b-4ffe-8165-109fd3a8b917",
"34d8dc6f-fdd1-4542-a038-c1235a5a97fa",
"12ad308b-6ce1-40ec-9ebf-f0215c30500e",
"e2e85b2e-a208-4a65-87fd-b92721b037a8",
"208a3618-7700-421b-bf05-fd59551cc1aa",
"d5ce7320-5b25-4ced-bda5-0e25a9d75940",
"9d14706f-cee7-4eb4-9151-2558609c3de0",
"3f4555ac-eaf3-4066-a1ba-20bb1ec1c0b3"
]
ndvi_list =
scene_id_list
|> Flow.from_enumerable(stages: 2, max_demand: 1)
|> Flow.map(fn scene_id ->
"/tmp/#{scene_id}"
|> File.ls!()
|> Enum.map(fn filename -> Path.join(["/tmp", scene_id, filename]) end)
|> NDVIClient.calc()
end)
|> Enum.to_list()
ndvi_list
|> Enum.map(fn {date, ndvi_tensor} ->
img =
Evision.applyColorMap(src: ndvi_tensor, colormap: Evision.Constant.cv_COLORMAP_WINTER())
|> Evision.cvtColor(Evision.Constant.cv_COLOR_RGB2BGR())
|> Evision.Mat.to_nx()
|> Kino.Image.new()
{date, img}
end)
|> Kino.Layout.tabs()