///|
pub struct HistogramSummary {
bins : Array[Int]
total : Int
min : Int
max : Int
} derive(Debug, Eq)
///|
pub fn HistogramSummary::from_image(
image : GrayImage,
bin_count? : Int = 256,
) -> HistogramSummary {
let count = max_i(1, bin_count)
let bins = Array::make(count, 0)
let mut low = 255
let mut high = 0
let mut total = 0
for value in image.pixels {
let index = min_i(count - 1, value * count / 256)
bins[index] += 1
low = min_i(low, value)
high = max_i(high, value)
total += 1
}
{
bins,
total,
min: if total == 0 {
0
} else {
low
},
max: if total == 0 {
0
} else {
high
},
}
}
///|
pub fn HistogramSummary::count_at(self : HistogramSummary, bin : Int) -> Int {
if bin < 0 || bin >= self.bins.length() {
0
} else {
self.bins[bin]
}
}
///|
pub fn HistogramSummary::cumulative_at(
self : HistogramSummary,
bin : Int,
) -> Int {
let mut total = 0
for i in 0.. Int {
if self.total == 0 {
0
} else {
let target = self.total *
clamp(percentile_per_1000, low=0, high=1000) /
1000
let mut cumulative = 0
let mut result = 0
for i, count in self.bins {
cumulative += count
if cumulative >= max_i(1, target) {
result = i * 256 / self.bins.length()
break
}
}
result
}
}
///|
pub fn HistogramSummary::entropy_x1000(self : HistogramSummary) -> Int {
if self.total == 0 {
0
} else {
let mut entropy = 0
for count in self.bins {
if count > 0 {
let probability = count * 1000 / self.total
entropy += probability * (1000 - probability) / 1000
}
}
entropy
}
}
///|
pub fn GrayImage::histogram_summary(
self : GrayImage,
bin_count? : Int = 256,
) -> HistogramSummary {
HistogramSummary::from_image(self, bin_count~)
}
///|
pub fn GrayImage::percentile(
self : GrayImage,
percentile_per_1000 : Int,
) -> Int {
self.histogram_summary().percentile(percentile_per_1000)
}
///|
pub fn GrayImage::otsu_threshold(self : GrayImage) -> Int {
let histogram = self.histogram_summary()
if histogram.total == 0 {
0
} else {
let mut total_sum = 0
for i, count in histogram.bins {
total_sum += i * count
}
let mut background_weight = 0
let mut background_sum = 0
let mut best_threshold = 0
let mut best_score = -1
for i, count in histogram.bins {
background_weight += count
background_sum += i * count
let foreground_weight = histogram.total - background_weight
if background_weight > 0 && foreground_weight > 0 {
let delta = background_sum * foreground_weight -
(total_sum - background_sum) * background_weight
let score = delta * delta / (background_weight * foreground_weight)
if score > best_score {
best_score = score
best_threshold = i
}
}
}
best_threshold
}
}
///|
pub fn GrayImage::equalize_histogram(self : GrayImage) -> GrayImage {
let histogram = self.histogram_summary()
let out = GrayImage::new(width=self.width, height=self.height)
if histogram.total == 0 {
out
} else {
for y in 0.. RegionSummary {
let clipped = clamp_rect(rect, image.size())
let mut count = 0
let mut total = 0
let mut low = 255
let mut high = 0
for y in clipped.y.. Int {
self.max - self.min
}
///|
pub fn compare_regions(
image : GrayImage,
expected : Rect,
actual : Rect,
) -> Int {
let a = region_summary(image, expected)
let b = region_summary(image, actual)
abs_i(a.mean_x1000 - b.mean_x1000) / 1000
}