otsu_threshold
result = otsu_threshold(x)
Input: x · Output: result
What it is
x is an Image, Vec, or Signal. Computes (does not apply) the classic Otsu optimal threshold from x's histogram (Image reads per-pixel luma; Vec/Signal read their own values).
Overloads
One call shape — this function is not overloaded.
| Code | Input | Output | Description |
result = otsu_threshold(x) | x Image, Vec, or Signalnot documented yet | result number | Returns a single number, the threshold level. Compose with threshold(x, otsu_threshold(x)) to segment. |
Examples
From the documentation, and run by the build -- the output and the figure below are what the engine actually produced. Simplest first.
# Three bright discs on a dark field, written out and read back. The blob
# count printed below is then checkable against an image the reader can
# see being built, rather than asserted about a file nobody has.
n = 60
field = zeros(n, n)
centres = [15, 15; 42, 20; 30, 45]
for i in 0 to n - 1
for j in 0 to n - 1
for k in 0 to 2
dy = i - centres[k, 0]
dx = j - centres[k, 1]
if dy * dy + dx * dx < 64
field[i, j] = 230
end if
end for
end for
end for
save_image("cells.bmp", image_from_matrix(field))
img = load_image("cells.bmp")
level = otsu_threshold(img)
mask = threshold(img, level)
clean = bwareaopen(mask, 20) # drop specks smaller than 20 px
imshow(clean)
labeled = bwlabel(clean)
print(labeled.count) # number of detected blobs
stats = regionprops(labeled)
for i = 1 to labeled.count
r = stats[i - 1]
print(r.label, r.area, r.centroid_x, r.centroid_y)
end for
3 1 193 15 15 2 193 45 30 3 193 20 42
from images
How it works
otsu_threshold(x) — computes (does NOT apply) the classic Otsu optimal threshold: Image reads its per-pixel BT.601 luma histogram; Vec/Signal read their own value histogram. A pure compute step, meant to compose with threshold(x, otsu_threshold(x)) to actually segment — see qu_core::threshold::otsu_threshold's doc comment for the algorithm itself. otsu(x) below is the compute+apply convenience for when the scalar threshold isn't needed on its own.
From the note above this builtin in the interpreter, where the reasoning was written down as it was built.