Introduction

Every camera sensor delivers shades of gray. Every vision pipeline eventually needs a clean yes/no decision: is this pixel foreground or background? That step is called binarization, and it demands a single number — a threshold TT — that splits the 256 possible brightness levels into two groups.

Pick TT too low and the background bleeds into your foreground. Pick it too high and fine details vanish. For decades, engineers set TT by eye. In 1979, Nobuyuki Otsu published a one-page paper that made the choice automatic and optimal: scan the histogram once, find the TT that maximizes the variance between the two resulting classes, and you are done.

The method is still the default in OpenCV, ImageJ, and every other serious imaging library today — not because nothing better exists for special cases, but because it works remarkably well on the enormous range of problems where foreground and background differ in brightness.

Try It

The demo below generates a synthetic grayscale image of a bright disk on a dark background. Drag the threshold slider to see manual binarization, then press Run Otsu to let the algorithm pick the threshold automatically by maximizing between-class variance.

<!-- {{c_html_intro}} -->
<p class="hint">{{hint_para}}</p>
<div class="canvases">
  <div class="canvas-wrap">
    <div class="canvas-label">{{label_original}}</div>
    <canvas id="cvOrig" width="160" height="120"></canvas>
  </div>
  <div class="canvas-wrap">
    <div class="canvas-label">{{label_binary}}</div>
    <canvas id="cvBin" width="160" height="120"></canvas>
  </div>
  <div class="canvas-wrap">
    <div class="canvas-label">{{label_variance}}</div>
    <canvas id="cvVar" width="160" height="120"></canvas>
  </div>
</div>
<div class="slider-row">
  <label for="thresh">{{label_threshold}}: <span id="threshVal">128</span></label>
  <input type="range" id="thresh" min="0" max="255" value="128">
</div>
<div class="status" id="status"></div>
<div class="btns">
  <button id="btnOtsu" type="button">{{btn_otsu}}</button>
  <button id="btnReset" type="button" class="ghost">{{btn_reset}}</button>
</div>
/* {{c_css_root}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.canvases { display: flex; gap: .6rem; flex-wrap: wrap; margin-bottom: .7rem; }
.canvas-wrap { display: flex; flex-direction: column; align-items: center; gap: .2rem; }
.canvas-label { font-size: .78rem; font-weight: 600; color: #556; text-transform: uppercase; letter-spacing: .04em; }
canvas { border: 1px solid #cdd9e3; border-radius: 6px; display: block; }
.slider-row { display: flex; align-items: center; gap: .7rem; margin: .4rem 0; }
.slider-row label { font-size: .9rem; white-space: nowrap; min-width: 10em; }
input[type=range] { flex: 1; accent-color: #1d3557; }
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin: .4rem 0; }
.status.ok { color: #0a7d33; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
// Code not found

Watch the variance curve above the slider: Otsu's threshold lands exactly at its peak. Moving the slider away from that peak always increases the overlap between the two brightness classes — the algorithm provably finds the global maximum in a single left-to-right scan.

The Math

Let L=256L = 256 be the number of gray levels and let pip_i be the fraction of pixels at level ii. Define cumulative sums up to a trial threshold tt:

ω0(t)=i=0tpi,μ0(t)=1ω0i=0tipi\omega_0(t) = \sum_{i=0}^{t} p_i, \quad \mu_0(t) = \frac{1}{\omega_0} \sum_{i=0}^{t} i \cdot p_i

and symmetrically ω1=1ω0\omega_1 = 1 - \omega_0, μ1=(μμ0ω0)/ω1\mu_1 = (\mu - \mu_0 \omega_0)/\omega_1 where μ\mu is the global mean. The between-class variance is:

σB2(t)=ω0(t)ω1(t)(μ0(t)μ1(t))2\sigma_B^2(t) = \omega_0(t)\,\omega_1(t)\,\bigl(\mu_0(t) - \mu_1(t)\bigr)^2

Otsu's algorithm simply finds T=argmaxtσB2(t)T^* = \arg\max_t \sigma_B^2(t).

Key facts:

  • Exact and parameter-free. The search is over 256 candidates — no gradient descent, no hyperparameters, no training data.
  • O(L)O(L) time after building the histogram. Each σB2(t)\sigma_B^2(t) is computed in O(1)O(1) from running totals updated left-to-right.
  • Equivalent to minimizing within-class variance. Because total variance is fixed, maximizing between-class variance is the same as making each class as tight as possible around its own mean.
  • Assumes a bimodal histogram. The formula is optimal when the image has two dominant brightness modes — a foreground cluster and a background cluster. Multimodal scenes require multi-threshold generalizations.

The 1979 paper is one of the most-cited works in computer vision precisely because this simple closed-form derivation delivers a robust, practical result with zero user tuning.

Where It Matters

Otsu's method sits at the front of a remarkable number of image-processing pipelines:

  • Document scanning and OCR: converting a photographed page to black-and-white is the first step before any text recognition. Otsu's threshold adapts to each image's lighting without manual calibration.
  • Medical imaging: segmenting bone from soft tissue in X-rays, or nuclei from cytoplasm in microscopy slides, often reduces to a brightness threshold — Otsu finds it automatically.
  • Industrial inspection: detecting defects (scratches, inclusions, missing parts) on a production line requires separating the defect brightness from the background. Speed matters here, and Otsu's O(L)O(L) pass delivers.
  • Satellite and remote sensing: distinguishing water bodies from land, or snow from soil, in multispectral images is a thresholding problem at scale.
  • Pre-processing for deeper algorithms: even modern neural-network training pipelines sometimes begin with a classical binarization step to reduce noise before feeding data to a learned model.

The algorithm's reach extends beyond grayscale: any 1-D distribution with two modes — audio silence detection, network packet size classification, anomaly scoring — admits the same between-class variance trick.

Conclusion

Otsu's thresholding is a rare example of a problem that is both practically important and theoretically clean. The criterion — maximize the variance between the two pixel classes — has a closed-form solution, runs in a single left-to-right scan of 256 histogram bins, and requires no parameters at all.

Its longevity comes from that combination: it is fast enough for real-time pipelines, accurate enough for clinical imaging, and simple enough to understand in a single sitting. The next time you scan a document, read a barcode, or open an X-ray, there is a good chance a descendant of Nobuyuki Otsu's 1979 formula is silently deciding which pixels are foreground and which are background — one pass, one threshold, one clean answer.

For scenes with more than two brightness modes, the method generalizes naturally to multi-threshold Otsu, and related ideas appear in dimensionality reduction and pattern matching whenever a clean boundary between classes is needed.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/otsu-thresholding/Content licensed under CC BY-NC 4.0.