///|
pub struct GradientPixel {
point : Point
gx : Int
gy : Int
magnitude : Int
} derive(Debug, Eq)
///|
pub fn GradientPixel::new(
point~ : Point,
gx~ : Int,
gy~ : Int,
) -> GradientPixel {
{ point, gx, gy, magnitude: abs_i(gx) + abs_i(gy) }
}
///|
pub struct GradientField {
width : Int
height : Int
pixels : Array[GradientPixel]
} derive(Debug, Eq)
///|
pub fn GradientField::from_image(image : GrayImage) -> GradientField {
let pixels : Array[GradientPixel] = []
for y in 0.. Int {
y * self.width + x
}
///|
pub fn GradientField::in_bounds(self : GradientField, x : Int, y : Int) -> Bool {
x >= 0 && y >= 0 && x < self.width && y < self.height
}
///|
pub fn GradientField::get(
self : GradientField,
x : Int,
y : Int,
) -> GradientPixel {
if self.in_bounds(x, y) {
self.pixels[self.index(x, y)]
} else {
GradientPixel::new(point=Point::new(x~, y~), gx=0, gy=0)
}
}
///|
pub fn GradientField::magnitude_image(self : GradientField) -> GrayImage {
let out = GrayImage::new(width=self.width, height=self.height)
for pixel in self.pixels {
out.set(pixel.point.x, pixel.point.y, Gray::new(pixel.magnitude))
}
out
}
///|
pub fn GradientField::direction_code(
self : GradientField,
x : Int,
y : Int,
) -> Int {
let pixel = self.get(x, y)
if abs_i(pixel.gx) >= abs_i(pixel.gy) {
if pixel.gx >= 0 {
0
} else {
4
}
} else if pixel.gy >= 0 {
2
} else {
6
}
}
///|
pub fn GradientField::mean_magnitude(self : GradientField) -> Int {
if self.pixels.length() == 0 {
0
} else {
let mut total = 0
for pixel in self.pixels {
total += pixel.magnitude
}
total / self.pixels.length()
}
}
///|
pub struct CornerFeature {
point : Point
score : Int
response : Int
} derive(Debug, Eq)
///|
pub fn CornerFeature::new(
point~ : Point,
score~ : Int,
response~ : Int,
) -> CornerFeature {
{ point, score, response }
}
///|
pub fn corner_response(
field : GradientField,
x : Int,
y : Int,
radius : Int,
) -> Int {
let r = max_i(1, radius)
let mut xx = 0
let mut yy = 0
let mut xy = 0
for dy in -r..<=r {
for dx in -r..<=r {
let pixel = field.get(x + dx, y + dy)
xx += pixel.gx * pixel.gx
yy += pixel.gy * pixel.gy
xy += pixel.gx * pixel.gy
}
}
let determinant = xx * yy - xy * xy
let trace = xx + yy
determinant - trace * trace / 25
}
///|
pub fn detect_corners(
image : GrayImage,
threshold : Int,
radius? : Int = 1,
border? : Int = 1,
) -> Array[CornerFeature] {
let field = GradientField::from_image(image)
let out : Array[CornerFeature] = []
let r = max_i(1, radius)
let b = max_i(r, border)
if image.width > b * 2 && image.height > b * 2 {
for y in b..<(image.height - b) {
for x in b..<(image.width - b) {
let response = corner_response(field, x, y, r)
if response > threshold && is_local_maximum(field, x, y, r, response) {
out.push(
CornerFeature::new(
point=Point::new(x~, y~),
score=response / max_i(1, r * r),
response~,
),
)
}
}
}
}
out
}
///|
fn is_local_maximum(
field : GradientField,
x : Int,
y : Int,
radius : Int,
response : Int,
) -> Bool {
for dy in -radius..<=radius {
for dx in -radius..<=radius {
if dx != 0 || dy != 0 {
if corner_response(field, x + dx, y + dy, 1) > response {
return false
}
}
}
}
true
}
///|
pub struct PatchDescriptor {
center : Point
radius : Int
values : Array[Int]
} derive(Debug, Eq)
///|
pub fn PatchDescriptor::from_image(
image : GrayImage,
center : Point,
radius : Int,
) -> PatchDescriptor {
let r = max_i(0, radius)
let values : Array[Int] = []
let center_value = image.get(center.x, center.y).value
for y in (center.y - r)..<=(center.y + r) {
for x in (center.x - r)..<=(center.x + r) {
values.push(image.get(x, y).value - center_value)
}
}
{ center, radius: r, values }
}
///|
pub fn PatchDescriptor::distance(
self : PatchDescriptor,
other : PatchDescriptor,
) -> Int {
let count = min_i(self.values.length(), other.values.length())
let mut total = 0
for i in 0.. Array[PatchDescriptor] {
let out : Array[PatchDescriptor] = []
for point in points {
out.push(PatchDescriptor::from_image(image, point, radius))
}
out
}
///|
pub fn projection_profile(
image : GrayImage,
horizontal : Bool,
cutoff : Int,
) -> Array[Int] {
let length = if horizontal { image.height } else { image.width }
let out = Array::make(max_i(0, length), 0)
for y in 0..= cutoff {
let index = if horizontal { y } else { x }
out[index] += 1
}
}
}
out
}
///|
pub fn find_runs(values : Array[Int], threshold : Int) -> Array[Rect] {
let out : Array[Rect] = []
let mut start = -1
for i, value in values {
if value >= threshold && start < 0 {
start = i
} else if value < threshold && start >= 0 {
out.push(Rect::new(x=start, y=0, width=i - start, height=1))
start = -1
}
}
if start >= 0 {
out.push(Rect::new(x=start, y=0, width=values.length() - start, height=1))
}
out
}
///|
pub fn detect_bright_regions(
image : GrayImage,
cutoff : Int,
minimum_area : Int,
) -> Array[Box2D] {
let binary = Mask::new(width=image.width, height=image.height)
for y in 0..= cutoff {
binary.set(x, y, 1)
}
}
}
let label = Label::new(id=1, name="bright-region")
let out : Array[Box2D] = []
for component in connected_components(binary, 1) {
if component.area >= minimum_area {
out.push(Box2D::new(label~, rect=component.bounds))
}
}
out
}
///|
pub fn image_edge_density(image : GrayImage, threshold : Int) -> Int {
let edges = image.edge_map(threshold)
let mut count = 0
for value in edges.pixels {
if value > 0 {
count += 1
}
}
if edges.pixels.length() == 0 {
0
} else {
count * 1000 / edges.pixels.length()
}
}
///|
pub fn keypoints_to_annotations(
points : Array[Point],
label : Label,
radius : Int,
) -> Array[InstanceAnnotation] {
let out : Array[InstanceAnnotation] = []
for i, point in points {
out.push(
InstanceAnnotation::new(
id=i,
label~,
bbox=Box2D::new(
label~,
rect=Rect::new(
x=point.x - radius,
y=point.y - radius,
width=radius * 2 + 1,
height=radius * 2 + 1,
),
),
keypoints=[Keypoint::new(label=label.name, point~, visible=true)],
),
)
}
out
}