///|
pub struct FlowStats {
count : Int
mean_dx : Int
mean_dy : Int
mean_magnitude : Int
max_magnitude : Int
out_of_bounds : Int
} derive(Debug, Eq)
///|
pub fn FlowStats::from_flow(flow : Array[FlowVector], size : Size) -> FlowStats {
let mut dx = 0
let mut dy = 0
let mut magnitude = 0
let mut high = 0
let mut outside = 0
for vector in flow {
dx += vector.dx
dy += vector.dy
let length = abs_i(vector.dx) + abs_i(vector.dy)
magnitude += length
high = max_i(high, length)
if vector.x < 0 ||
vector.y < 0 ||
vector.x >= size.width ||
vector.y >= size.height {
outside += 1
}
}
let count = flow.length()
{
count,
mean_dx: dx / max_i(1, count),
mean_dy: dy / max_i(1, count),
mean_magnitude: magnitude / max_i(1, count),
max_magnitude: high,
out_of_bounds: outside,
}
}
///|
pub fn flow_divergence(flow : Array[FlowVector], size : Size) -> Int {
let mut total = 0
let mut count = 0
for vector in flow {
if vector.x > 0 &&
vector.y > 0 &&
vector.x + 1 < size.width &&
vector.y + 1 < size.height {
total += vector.dx + vector.dy
count += 1
}
}
total / max_i(1, count)
}
///|
pub fn flow_curl(flow : Array[FlowVector], size : Size) -> Int {
let mut total = 0
let mut count = 0
for vector in flow {
if vector.x > 0 &&
vector.y > 0 &&
vector.x + 1 < size.width &&
vector.y + 1 < size.height {
total += vector.dy - vector.dx
count += 1
}
}
total / max_i(1, count)
}
///|
pub fn flow_magnitude_image(
flow : Array[FlowVector],
size : Size,
scale : Int,
) -> GrayImage {
let out = GrayImage::new(width=size.width, height=size.height)
let factor = max_i(1, scale)
for vector in flow {
if out.in_bounds(vector.x, vector.y) {
out.set(vector.x, vector.y, Gray::new(flow_magnitude_l1(vector) * factor))
}
}
out
}
///|
pub fn flow_direction_image(flow : Array[FlowVector], size : Size) -> GrayImage {
let out = GrayImage::new(width=size.width, height=size.height)
for vector in flow {
if out.in_bounds(vector.x, vector.y) {
let horizontal = vector.dx + 255
let vertical = vector.dy + 255
out.set(vector.x, vector.y, Gray::new((horizontal + vertical) / 2))
}
}
out
}
///|
pub fn flow_confidence(
flow : Array[FlowVector],
size : Size,
max_length : Int,
) -> GrayImage {
let out = GrayImage::new(width=size.width, height=size.height)
let maximum = max_i(1, max_length)
for vector in flow {
if out.in_bounds(vector.x, vector.y) {
let length = abs_i(vector.dx) + abs_i(vector.dy)
out.set(vector.x, vector.y, Gray::new(255 - length * 255 / maximum))
}
}
out
}
///|
pub fn flow_endpoint_error_l1(
expected : Array[FlowVector],
actual : Array[FlowVector],
) -> Int {
let count = min_i(expected.length(), actual.length())
if count == 0 {
0
} else {
let mut total = 0
for i in 0.. Int {
let count = min_i(expected.length(), actual.length())
if count == 0 {
0
} else {
let mut total = 0
for i in 0.. Int {
let count = min_i(expected.length(), actual.length())
if count == 0 {
1000
} else {
let mut good = 0
for i in 0.. RgbImage {
let out = RgbImage::new(width=size.width, height=size.height)
let factor = max_i(1, scale)
for vector in flow {
if out.in_bounds(vector.x, vector.y) {
let red = 128 + vector.dx * factor
let green = 128 + vector.dy * factor
let blue = 255 - abs_i(vector.dx + vector.dy) * factor
out.set(vector.x, vector.y, Rgb::new(r=red, g=green, b=blue))
}
}
out
}
///|
pub fn flow_to_json_summary(flow : Array[FlowVector], size : Size) -> String {
let stats = FlowStats::from_flow(flow, size)
"{\"count\":\{stats.count},\"mean_dx\":\{stats.mean_dx},\"mean_dy\":\{stats.mean_dy},\"mean_magnitude\":\{stats.mean_magnitude},\"max_magnitude\":\{stats.max_magnitude},\"out_of_bounds\":\{stats.out_of_bounds}}"
}
///|
pub fn validate_flow(flow : Array[FlowVector], size : Size) -> ValidationReport {
let report = ValidationReport::new()
for vector in flow {
if !size.is_valid() {
report.error("flow target image size must be positive")
}
if vector.x < 0 ||
vector.y < 0 ||
vector.x >= size.width ||
vector.y >= size.height {
report.warning("flow origin is outside the target image")
}
if abs_i(vector.dx) > size.width || abs_i(vector.dy) > size.height {
report.warning("flow displacement exceeds image extent")
}
}
report
}