Lably
Skip to content

Otsu's method

In computer vision and image processing

  • 18 min read
  • 2 chapters

Related on Lably

Through Computer vision, Digital image processing and K-means clustering

Otsu's method is a one-dimensional discrete analogue of Fisher's discriminant analysis, is related to Jenks optimization method, and is equivalent to a globally optimal k-means performed on the intensity histogram. The extension to multi-level thresholding was described in the original paper, and computationally efficient implementations have since been proposed.

Otsu's method

Let, H be the normalised histogram of the pixels in an image (s.t. it becomes the probability distribution of pixel intensities) with L bins. There are two classes of this histogram: C0 for background pixels, and C1 for foreground pixels. The primary disciminator of pixels (to assort them into classes) is the threshold t. C0 includes pixels from 0 to (t−1), and C1 includes from t to (L−1).

The algorithm is then global search for an optimal threshold t∗ such that intra-class variance (variance of pixels intensities in C0 or C1) is minimised.

Let, ω0 denote the cumulative probability of C0, and ω1denote of C1.ω0(t)=∑i=0t−1P(i),ω1(t)=∑i=tL−1P(i).For a classes C0 and C1, the conditional probability of selecting the i-th pixel in those classes is P(i|C0) and P(i|C1) respectively.

Now, let μ0(t) and μ1(t) be the mean (pixel intensity) of C0 and C1 respectively.

μ0(t)=∑i=0t−1iP(i|C0)=∑i=0t−1iP(i)ω0(t)=∑i=0t−1iP(i)ω0(t).

Similarly,

μ1(t)=∑i=tL−1iP(i)ω1(t).

Now, let σ02(t) and σ12(t) be the (pixel intensity) variance of C0 and C1 respectively.

σ02(t)=∑i=0t−1(i−μ0)2P(i|C0)=∑i=0t−1(i−μ0)2P(i)ω0=∑i=0t−1(i−μ0)2P(i)ω0(t).

Similarly,

σ12(t)=∑i=tL−1(i−μ1)2P(i)ω1(t).

Let, σb2(t) be the inter-class (pixel intensity) variance, which is defined as the weighted sum of variances of aforementioned two classes.

σb2(t)=σT2−[ω0(t)σ02(t)+ω1(t)σ12(t)]=ω0(μ0−μT)2+w1(μ1−μT)2=ω0ω1(μ0−μ1)2.

Where, σT2(t) variance of the total histogram.

Proof

Considering ω0+ω1=1 and ω0μ0+ω1μ1=μT, we can prove the following.

σb2(t)=ω0(μ0−μT)2+w1(μ1−μT)2=ω0μ02+ω1μ12−2μT(ω0μ0+ω1μ1)+μT2(ω0+ω1)=ω0μ02+ω1μ12−2μT2+μT2=ω0μ02+ω1μ12−μT2=ω0μ02+ω1μ12−(ω0μ0+ω1μ1)2=ω0μ02−ω02μ02+ω1μ12−ω12μ12−2ω0μ0ω1μ1=ω0μ02(1−ω0)+ω1μ12(1−ω1)−2ω0μ0ω1μ1=ω0μ02(1−ω0)−ω0μ0ω1μ1+ω1μ12(1−ω1)−ω0μ0ω1μ1=ω0ω1μ02−ω0ω1μ0μ1+ω0ω1μ12−ω0ω1μ0μ1=ω0ω1(μ02−μ0μ1)+ω0ω1(μ12−μ0μ1)=ω0ω1(μ02−2μ0μ1+μ12)=ω0ω1(μ0−μ1)2.

The algorithm is now to maximise σb2(t), i.e. inter-class variance. This standpoint is motivated by a conjecture that well-thresholded classes would be separated in pixel intensities, and conversely a threshold t∗ giving the best separation of classes in pixel intensities would be the best threshold.

Formally, this problem is summarised as the following.

σb2(t∗)=max0<t<Lσb2(t)

Algorithm

  1. Compute histogram and probabilities of each intensity level.
  2. Set up initial ω0(0), μ0(0) and ω1(0) and μ1(0).
  3. Step through all possible thresholds from t=1 to maximum intensity.
    1. Update ω0(0), μ0(0) and ω1(0) and μ1(0).
    2. Compute σb2(t).
  4. Desired threshold t∗ corresponds to the maximum σb2(t).

MATLAB implementation

histogramCounts is a 256-element histogram of a grayscale image different gray-levels (typical for 8-bit images). level is the threshold for the image (double).

<span id="mwkg"></span><span class="k" id="mwkw">function</span><span class="w" id="mwlA"> </span>level<span class="w" id="mwlQ"> </span><span class="p" id="mwlg">=</span><span class="w" id="mwlw"> </span><span class="nf" id="mwmA">otsu</span><span class="p" id="mwmQ">(</span>histogramCounts<span class="p" id="mwmg">)</span> <span class="n" id="mwmw">total</span><span class="w" id="mwnA"> </span><span class="p" id="mwnQ">=</span><span class="w" id="mwng"> </span><span class="nb" id="mwnw">sum</span><span class="p" id="mwoA">(</span><span class="n" id="mwoQ">histogramCounts</span><span class="p" id="mwog">);</span><span class="w" id="mwow"> </span><span class="c" id="mwpA">% total number of pixels in the image </span> <span class="c" id="mwpQ">%% OTSU automatic thresholding</span> <span class="n" id="mwpg">top</span><span class="w" id="mwpw"> </span><span class="p" id="mwqA">=</span><span class="w" id="mwqQ"> </span><span class="mi" id="mwqg">256</span><span class="p" id="mwqw">;</span> <span class="n" id="mwrA">sumB</span><span class="w" id="mwrQ"> </span><span class="p" id="mwrg">=</span><span class="w" id="mwrw"> </span><span class="mi" id="mwsA">0</span><span class="p" id="mwsQ">;</span> <span class="n" id="mwsg">wB</span><span class="w" id="mwsw"> </span><span class="p" id="mwtA">=</span><span class="w" id="mwtQ"> </span><span class="mi" id="mwtg">0</span><span class="p" id="mwtw">;</span> <span class="n" id="mwuA">maximum</span><span class="w" id="mwuQ"> </span><span class="p" id="mwug">=</span><span class="w" id="mwuw"> </span><span class="mf" id="mwvA">0.0</span><span class="p" id="mwvQ">;</span> <span class="n" id="mwvg">sum1</span><span class="w" id="mwvw"> </span><span class="p" id="mwwA">=</span><span class="w" id="mwwQ"> </span><span class="nb" id="mwwg">dot</span><span class="p" id="mwww">(</span><span class="mi" id="mwxA">0</span><span class="p" id="mwxQ">:</span><span class="n" id="mwxg">top</span><span class="o" id="mwxw">-</span><span class="mi" id="mwyA">1</span><span class="p" id="mwyQ">,</span><span class="w" id="mwyg"> </span><span class="n" id="mwyw">histogramCounts</span><span class="p" id="mwzA">);</span> <span class="k" id="mwzQ">for</span><span class="w" id="mwzg"> </span><span class="n" id="mwzw">ii</span><span class="w" id="mw0A"> </span><span class="p" id="mw0Q">=</span><span class="w" id="mw0g"> </span><span class="mi" id="mw0w">1</span><span class="p" id="mw1A">:</span><span class="n" id="mw1Q">top</span> <span class="w" id="mw1g"> </span><span class="n" id="mw1w">wB</span><span class="w" id="mw2A"> </span><span class="p" id="mw2Q">=</span><span class="w" id="mw2g"> </span><span class="n" id="mw2w">wB</span><span class="w" id="mw3A"> </span><span class="o" id="mw3Q">+</span><span class="w" id="mw3g"> </span><span class="n" id="mw3w">histogramCounts</span><span class="p" id="mw4A">(</span><span class="n" id="mw4Q">ii</span><span class="p" id="mw4g">);</span> <span class="w" id="mw4w"> </span><span class="n" id="mw5A">wF</span><span class="w" id="mw5Q"> </span><span class="p" id="mw5g">=</span><span class="w" id="mw5w"> </span><span class="n" id="mw6A">total</span><span class="w" id="mw6Q"> </span><span class="o" id="mw6g">-</span><span class="w" id="mw6w"> </span><span class="n" id="mw7A">wB</span><span class="p" id="mw7Q">;</span> <span class="w" id="mw7g"> </span><span class="n" id="mw7w">sumB</span><span class="w" id="mw8A"> </span><span class="p" id="mw8Q">=</span><span class="w" id="mw8g"> </span><span class="n" id="mw8w">sumB</span><span class="w" id="mw9A"> </span><span class="o" id="mw9Q">+</span><span class="w" id="mw9g"> </span><span class="p" id="mw9w">(</span><span class="n" id="mw-A">ii</span><span class="o" id="mw-Q">-</span><span class="mi" id="mw-g">1</span><span class="p" id="mw-w">)</span><span class="w" id="mw_A"> </span><span class="o" id="mw_Q">*</span><span class="w" id="mw_g"> </span><span class="n" id="mw_w">histogramCounts</span><span class="p" id="mwAQA">(</span><span class="n" id="mwAQE">ii</span><span class="p" id="mwAQI">);</span> <span class="w" id="mwAQM"> </span><span class="k" id="mwAQQ">if</span><span class="w" id="mwAQU"> </span><span class="n" id="mwAQY">wB</span><span class="w" id="mwAQc"> </span><span class="o" id="mwAQg">></span><span class="w" id="mwAQk"> </span><span class="mi" id="mwAQo">0</span><span class="w" id="mwAQs"> </span><span class="o" id="mwAQw">&&</span><span class="w" id="mwAQ0"> </span><span class="n" id="mwAQ4">wF</span><span class="w" id="mwAQ8"> </span><span class="o" id="mwARA">></span><span class="w" id="mwARE"> </span><span class="mi" id="mwARI">0</span> <span class="w" id="mwARM"> </span><span class="n" id="mwARQ">mF</span><span class="w" id="mwARU"> </span><span class="p" id="mwARY">=</span><span class="w" id="mwARc"> </span><span class="p" id="mwARg">(</span><span class="n" id="mwARk">sum1</span><span class="w" id="mwARo"> </span><span class="o" id="mwARs">-</span><span class="w" id="mwARw"> </span><span class="n" id="mwAR0">sumB</span><span class="p" id="mwAR4">)</span><span class="w" id="mwAR8"> </span><span class="o" id="mwASA">/</span><span class="w" id="mwASE"> </span><span class="n" id="mwASI">wF</span><span class="p" id="mwASM">;</span> <span class="w" id="mwASQ"> </span><span class="n" id="mwASU">val</span><span class="w" id="mwASY"> </span><span class="p" id="mwASc">=</span><span class="w" id="mwASg"> </span><span class="n" id="mwASk">wB</span><span class="w" id="mwASo"> </span><span class="o" id="mwASs">*</span><span class="w" id="mwASw"> </span><span class="n" id="mwAS0">wF</span><span class="w" id="mwAS4"> </span><span class="o" id="mwAS8">*</span><span class="w" id="mwATA"> </span><span class="p" id="mwATE">((</span><span class="n" id="mwATI">sumB</span><span class="w" id="mwATM"> </span><span class="o" id="mwATQ">/</span><span class="w" id="mwATU"> </span><span class="n" id="mwATY">wB</span><span class="p" id="mwATc">)</span><span class="w" id="mwATg"> </span><span class="o" id="mwATk">-</span><span class="w" id="mwATo"> </span><span class="n" id="mwATs">mF</span><span class="p" id="mwATw">)</span><span class="w" id="mwAT0"> </span><span class="o" id="mwAT4">*</span><span class="w" id="mwAT8"> </span><span class="p" id="mwAUA">((</span><span class="n" id="mwAUE">sumB</span><span class="w" id="mwAUI"> </span><span class="o" id="mwAUM">/</span><span class="w" id="mwAUQ"> </span><span class="n" id="mwAUU">wB</span><span class="p" id="mwAUY">)</span><span class="w" id="mwAUc"> </span><span class="o" id="mwAUg">-</span><span class="w" id="mwAUk"> </span><span class="n" id="mwAUo">mF</span><span class="p" id="mwAUs">);</span> <span class="w" id="mwAUw"> </span><span class="k" id="mwAU0">if</span><span class="w" id="mwAU4"> </span><span class="p" id="mwAU8">(</span><span class="w" id="mwAVA"> </span><span class="n" id="mwAVE">val</span><span class="w" id="mwAVI"> </span><span class="o" id="mwAVM">>=</span><span class="w" id="mwAVQ"> </span><span class="n" id="mwAVU">maximum</span><span class="w" id="mwAVY"> </span><span class="p" id="mwAVc">)</span> <span class="w" id="mwAVg"> </span><span class="n" id="mwAVk">level</span><span class="w" id="mwAVo"> </span><span class="p" id="mwAVs">=</span><span class="w" id="mwAVw"> </span><span class="n" id="mwAV0">ii</span><span class="p" id="mwAV4">;</span> <span class="w" id="mwAV8"> </span><span class="n" id="mwAWA">maximum</span><span class="w" id="mwAWE"> </span><span class="p" id="mwAWI">=</span><span class="w" id="mwAWM"> </span><span class="n" id="mwAWQ">val</span><span class="p" id="mwAWU">;</span> <span class="w" id="mwAWY"> </span><span class="k" id="mwAWc">end</span> <span class="w" id="mwAWg"> </span><span class="k" id="mwAWk">end</span> <span class="k" id="mwAWo">end</span> <span class="k" id="mwAWs">end</span>

Matlab has built-in functions graythresh() and multithresh() in the Image Processing Toolbox, which are implemented with Otsu's method and multi-Otsu's method, respectively.

Python implementation

This implementation requires the NumPy library.

<span id="mwAXU"></span><span class="kn" id="mwAXY">import</span><span class="w" id="mwAXc"> </span><span class="nn" id="mwAXg">numpy</span><span class="w" id="mwAXk"> </span><span class="k" id="mwAXo">as</span><span class="w" id="mwAXs"> </span><span class="nn" id="mwAXw">np</span> <span class="k" id="mwAX0">def</span><span class="w" id="mwAX4"> </span><span class="nf" id="mwAX8">otsu_intraclass_variance</span><span class="p" id="mwAYA">(</span><span class="n" id="mwAYE">image</span><span class="p" id="mwAYI">,</span> <span class="n" id="mwAYM">threshold</span><span class="p" id="mwAYQ">):</span> <span class="w" id="mwAYU"> </span><span class="sd" id="mwAYY">"""</span> <span class="sd" id="mwAYc"> Otsu's intra-class variance.</span> <span class="sd" id="mwAYg"> If all pixels are above or below the threshold, this will throw a warning that can safely be ignored.</span> <span class="sd" id="mwAYk"> """</span> <span class="k" id="mwAYo">return</span> <span class="n" id="mwAYs">np</span><span class="o" id="mwAYw">.</span><span class="n" id="mwAY0">nansum</span><span class="p" id="mwAY4">(</span> <span class="p" id="mwAY8">[</span> <span class="n" id="mwAZA">np</span><span class="o" id="mwAZE">.</span><span class="n" id="mwAZI">mean</span><span class="p" id="mwAZM">(</span><span class="bp" id="mwAZQ">cls</span><span class="p" id="mwAZU">)</span> <span class="o" id="mwAZY">*</span> <span class="n" id="mwAZc">np</span><span class="o" id="mwAZg">.</span><span class="n" id="mwAZk">var</span><span class="p" id="mwAZo">(</span><span class="n" id="mwAZs">image</span><span class="p" id="mwAZw">,</span> <span class="n" id="mwAZ0">where</span><span class="o" id="mwAZ4">=</span><span class="bp" id="mwAZ8">cls</span><span class="p" id="mwAaA">)</span> <span class="c1" id="mwAaE"># weight · intra-class variance</span> <span class="k" id="mwAaI">for</span> <span class="bp" id="mwAaM">cls</span> <span class="ow" id="mwAaQ">in</span> <span class="p" id="mwAaU">[</span><span class="n" id="mwAaY">image</span> <span class="o" id="mwAac">>=</span> <span class="n" id="mwAag">threshold</span><span class="p" id="mwAak">,</span> <span class="n" id="mwAao">image</span> <span class="o" id="mwAas"><</span> <span class="n" id="mwAaw">threshold</span><span class="p" id="mwAa0">]</span> <span class="p" id="mwAa4">]</span> <span class="p" id="mwAa8">)</span> <span class="c1" id="mwAbA"># NaNs only arise if the class is empty, in which case the contribution should be zero, which `nansum` accomplishes.</span> <span class="c1" id="mwAbE"># Random image for demonstration:</span> <span class="n" id="mwAbI">image</span> <span class="o" id="mwAbM">=</span> <span class="n" id="mwAbQ">np</span><span class="o" id="mwAbU">.</span><span class="n" id="mwAbY">random</span><span class="o" id="mwAbc">.</span><span class="n" id="mwAbg">randint</span><span class="p" id="mwAbk">(</span><span class="mi" id="mwAbo">2</span><span class="p" id="mwAbs">,</span> <span class="mi" id="mwAbw">253</span><span class="p" id="mwAb0">,</span> <span class="n" id="mwAb4">size</span><span class="o" id="mwAb8">=</span><span class="p" id="mwAcA">(</span><span class="mi" id="mwAcE">50</span><span class="p" id="mwAcI">,</span> <span class="mi" id="mwAcM">50</span><span class="p" id="mwAcQ">))</span> <span class="n" id="mwAcU">otsu_threshold</span> <span class="o" id="mwAcY">=</span> <span class="nb" id="mwAcc">min</span><span class="p" id="mwAcg">(</span> <span class="nb" id="mwAck">range</span><span class="p" id="mwAco">(</span><span class="n" id="mwAcs">np</span><span class="o" id="mwAcw">.</span><span class="n" id="mwAc0">min</span><span class="p" id="mwAc4">(</span><span class="n" id="mwAc8">image</span><span class="p" id="mwAdA">)</span> <span class="o" id="mwAdE">+</span> <span class="mi" id="mwAdI">1</span><span class="p" id="mwAdM">,</span> <span class="n" id="mwAdQ">np</span><span class="o" id="mwAdU">.</span><span class="n" id="mwAdY">max</span><span class="p" id="mwAdc">(</span><span class="n" id="mwAdg">image</span><span class="p" id="mwAdk">)),</span> <span class="n" id="mwAdo">key</span><span class="o" id="mwAds">=</span><span class="k" id="mwAdw">lambda</span> <span class="n" id="mwAd0">th</span><span class="p" id="mwAd4">:</span> <span class="n" id="mwAd8">otsu_intraclass_variance</span><span class="p" id="mwAeA">(</span><span class="n" id="mwAeE">image</span><span class="p" id="mwAeI">,</span> <span class="n" id="mwAeM">th</span><span class="p" id="mwAeQ">),</span> <span class="p" id="mwAeU">)</span>

Python libraries dedicated to image processing such as OpenCV and Scikit-image provide built-in implementations of the algorithm.

Sign in to keep reading

One more chapter is free with a Lably account.

More in Robotics