Thresholding with Otsu's method

Thresholding turns a grayscale image into a binary one: pixels above a threshold become foreground (1) and the rest become background (0). It is a basic step in document scanning, microscopy, and many segmentation pipelines. The difficulty is choosing the threshold, since a fixed value fails when lighting changes.

Python
def otsu(pixels):
    histogram = [0] * 256
    for pixel in pixels:
        histogram[pixel] += 1
    total = len(pixels)
    total_sum = sum(level * count for level, count in enumerate(histogram))
    best_variance, best_threshold = -1.0, 0
    weight_dark = sum_dark = 0
    for t in range(256):
        weight_dark += histogram[t]
        if weight_dark == 0:
            continue
        weight_bright = total - weight_dark
        if weight_bright == 0:
            break
        sum_dark += t * histogram[t]
        mean_dark = sum_dark / weight_dark
        mean_bright = (total_sum - sum_dark) / weight_bright
        between = weight_dark * weight_bright * (mean_dark - mean_bright) ** 2
        if between > best_variance:
            best_variance, best_threshold = between, t
    return best_threshold


pixels = [28, 30, 35, 40, 33, 31, 38, 29, 205, 210, 215, 200, 220, 208, 212, 218]
threshold = otsu(pixels)
print(threshold)
print([1 if pixel > threshold else 0 for pixel in pixels])
Read the full lesson