///|
pub(all) struct SampleDifference {
samples : Int32
changed_bits : Int32
finite_pairs : Int32
maximum_absolute_error : Double?
} derive(Debug)
///|
pub extend SampleDifference with @debug.Debug::{to_repr}
///|
/// Compare identical channel types/windows, keeping NaN payload and signed-zero differences.
pub fn Plane::compare_samples(
self : Plane,
other : Plane,
) -> SampleDifference raise ExrError {
if self.window != other.window || self.channel.kind != other.channel.kind {
raise Invalid("comparison window or sample type", 0)
}
let (xmin, ymin, xmax, ymax) = self.window.bounds()
let mut changed = 0
let mut pairs = 0
let mut maximum = 0.0
for y = ymin; y <= ymax; y = y + 1 {
for x = xmin; x <= xmax; x = x + 1 {
if self.bits_at(x, y) != other.bits_at(x, y) {
changed += 1
}
let a = self.value_at(x, y)
let b = other.value_at(x, y)
if finite(a) && finite(b) {
pairs += 1
let delta = if a >= b { a - b } else { b - a }
if delta > maximum {
maximum = delta
}
}
// Avoid incrementing an Int32 maximum coordinate.
if x == xmax {
break
}
}
if y == ymax {
break
}
}
{
samples: self.window.pixels(),
changed_bits: changed,
finite_pairs: pairs,
maximum_absolute_error: if pairs > 0 {
Some(maximum)
} else {
None
},
}
}
///|
pub(all) struct MaskedStatistics {
selected : Int32
finite_samples : Int32
nonfinite_samples : Int32
minimum : Double?
maximum : Double?
mean : Double?
} derive(Debug)
///|
pub extend MaskedStatistics with @debug.Debug::{to_repr}
///|
/// Nonzero UINT32 mask selects samples. No thresholding or modification of stored planes.
pub fn Plane::masked_statistics(
self : Plane,
mask : Plane,
) -> MaskedStatistics raise ExrError {
if self.window != mask.window || mask.channel.kind != U32 {
raise Invalid("mask needs same window and UINT32 samples", 0)
}
let (xmin, ymin, xmax, ymax) = self.window.bounds()
let mut selected = 0
let mut count = 0
let mut minimum : Double? = None
let mut maximum : Double? = None
let mut mean = 0.0
for y = ymin; y <= ymax; y = y + 1 {
for x = xmin; x <= xmax; x = x + 1 {
if mask.bits_at(x, y) != 0U {
selected += 1
let value = self.value_at(x, y)
if finite(value) {
count += 1
minimum = Some(
match minimum {
Some(m) => if value < m { value } else { m }
None => value
},
)
maximum = Some(
match maximum {
Some(m) => if value > m { value } else { m }
None => value
},
)
// Weighted form avoids overflowing value - mean for opposite extremes.
mean = mean * ((count - 1).to_double() / count.to_double()) +
value / count.to_double()
}
}
if x == xmax {
break
}
}
if y == ymax {
break
}
}
{
selected,
finite_samples: count,
nonfinite_samples: selected - count,
minimum,
maximum,
mean: if count > 0 && finite(mean) {
Some(mean)
} else {
None
},
}
}