///|
pub enum BorderMode {
Clamp
Reflect
Constant(Gray)
} derive(Debug, Eq)
///|
pub struct Kernel {
width : Int
height : Int
values : Array[Int]
} derive(Debug, Eq)
///|
pub fn Kernel::new(width~ : Int, height~ : Int, values~ : Array[Int]) -> Kernel {
let expected = max_i(0, width * height)
let normalized = Array::make(expected, 0)
for i in 0.. Int {
if x >= 0 && y >= 0 && x < self.width && y < self.height {
self.values[y * self.width + x]
} else {
0
}
}
///|
pub fn Kernel::sum(self : Kernel) -> Int {
let mut result = 0
for value in self.values {
result += value
}
result
}
///|
pub fn Kernel::scale(
self : Kernel,
numerator : Int,
denominator : Int,
) -> Kernel {
let den = if denominator == 0 { 1 } else { denominator }
let values : Array[Int] = []
for value in self.values {
values.push(value * numerator / den)
}
Kernel::new(width=self.width, height=self.height, values~)
}
///|
pub fn Kernel::box_filter(width : Int, height : Int) -> Kernel {
let w = max_i(1, width)
let h = max_i(1, height)
Kernel::new(width=w, height=h, values=Array::make(w * h, 1))
}
///|
pub fn Kernel::sharpen() -> Kernel {
Kernel::new(width=3, height=3, values=[0, -1, 0, -1, 5, -1, 0, -1, 0])
}
///|
pub fn Kernel::sobel_x() -> Kernel {
Kernel::new(width=3, height=3, values=[-1, 0, 1, -2, 0, 2, -1, 0, 1])
}
///|
pub fn Kernel::sobel_y() -> Kernel {
Kernel::new(width=3, height=3, values=[-1, -2, -1, 0, 0, 0, 1, 2, 1])
}
///|
fn reflect_index(index : Int, length : Int) -> Int {
if length <= 1 {
0
} else {
let period = length * 2 - 2
let wrapped = (index % period + period) % period
if wrapped < length {
wrapped
} else {
period - wrapped
}
}
}
///|
fn sample_with_border(
image : GrayImage,
x : Int,
y : Int,
border : BorderMode,
) -> Int {
if image.in_bounds(x, y) {
image.get(x, y).value
} else {
match border {
Clamp =>
image.get(
clamp(x, low=0, high=max_i(0, image.width - 1)),
clamp(y, low=0, high=max_i(0, image.height - 1)),
).value
Reflect =>
image.get(reflect_index(x, image.width), reflect_index(y, image.height)).value
Constant(gray) => gray.value
}
}
}
///|
fn convolve_at(
image : GrayImage,
kernel : Kernel,
x : Int,
y : Int,
border : BorderMode,
) -> Int {
let cx = kernel.width / 2
let cy = kernel.height / 2
let mut sum = 0
for ky in 0.. GrayImage {
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let total = kernel.sum()
if total == 0 {
self.convolve(kernel, border~)
} else {
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let kernel = Kernel::box_filter(width, height)
let total = max_i(1, kernel.sum())
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let kernel = Kernel::box_filter(width, height)
let total = max_i(1, kernel.sum())
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let kernel = Kernel::box_filter(width, height)
let total = max_i(1, kernel.sum())
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. Unit {
values.push(value)
let mut i = values.length() - 1
while i > 0 && values[i - 1] > value {
values[i] = values[i - 1]
i -= 1
}
values[i] = value
}
///|
pub fn GrayImage::median_filter(
self : GrayImage,
radius : Int,
border? : BorderMode = Clamp,
) -> GrayImage {
let r = max_i(0, radius)
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let blurred = self.mean_filter(radius * 2 + 1, radius * 2 + 1)
let amount = clamp(amount_per_100, low=0, high=500)
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let window = max_i(1, radius * 2 + 1)
let neighborhood = self.mean_filter(window, window)
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0..= threshold {
Gray::white()
} else {
Gray::black()
},
)
}
}
out
}
///|
pub fn GrayImage::local_contrast(self : GrayImage, radius : Int) -> GrayImage {
let blurred = self.mean_filter(radius * 2 + 1, radius * 2 + 1)
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0.. GrayImage {
let weight = clamp(weight_per_1000, low=0, high=1000)
let out = GrayImage::new(width=self.width, height=self.height)
for y in 0..