title: Implementing Otsu binarization from scratch python
body: <p>It seems my implementation is incorrect and not sure what exactly I'm doing wrong:</p>
  
  <p>Here is the histogram of my image:
  <a href="https://i.stack.imgur.com/2Ry8h.png" rel="noreferrer"><img src="https://i.stack.imgur.com/2Ry8h.png" alt="enter image description here"></a></p>
  
  <p>So the threshold should be around 170 ish? I'm getting the threshold as 130.</p>
  
  <p>Here is my code:</p>
  
  <pre><code>#Otsu in Python
  
  import numpy as np
  from PIL import Image
  import matplotlib.pyplot as plt  
  
  
  def load_image(file_name):
      img = Image.open(file_name)
      img.load()
      bw = img.convert('L')
      bw_data = np.array(bw).astype('int32')
      BINS = np.array(range(0,257))
      counts, pixels =np.histogram(bw_data, BINS)
      pixels = pixels[:-1]
      plt.bar(pixels, counts, align='center')
      plt.savefig('histogram.png')
      plt.xlim(-1, 256)
      plt.show()
  
      total_counts = np.sum(counts)
      assert total_counts == bw_data.shape[0]*bw_data.shape[1]
  
      return BINS, counts, pixels, bw_data, total_counts
  
  def within_class_variance():
      ''' Here we will implement the algorithm and find the lowest Within-  Class Variance:
  
          Refer to this page for more details http://www.labbookpages.co.uk
  /software/imgProc/otsuThreshold.html'''
  
      for i in range(1,len(BINS), 1):         #from one to 257 = 256 iterations
         prob_1 =    np.sum(counts[:i])/total_counts
         prob_2 = np.sum(counts[i:])/total_counts
         assert (np.sum(prob_1 + prob_2)) == 1.0
  
  
  
         mean_1 = np.sum(counts[:i] * pixels[:i])/np.sum(counts[:i])
         mean_2 = np.sum(counts[i:] * pixels[i:] )/np.sum(counts[i:])
         var_1 = np.sum(((pixels[:i] - mean_1)**2 ) * counts[:i])/np.sum(counts[:i])
         var_2 = np.sum(((pixels[i:] - mean_2)**2 ) * counts[i:])/np.sum(counts[i:])
  
  
         if i == 1:
           cost = (prob_1 * var_1) + (prob_2 * var_2)
           keys = {'cost': cost, 'mean_1': mean_1, 'mean_2': mean_2, 'var_1': var_1, 'var_2': var_2, 'pixel': i-1}
           print('first_cost',cost)
  
  
         if (prob_1 * var_1) +(prob_2 * var_2) &lt; cost:
           cost =(prob_1 * var_1) +(prob_2 * var_2)
           keys = {'cost': cost, 'mean_1': mean_1, 'mean_2': mean_2, 'var_1': var_1, 'var_2': var_2, 'pixel': i-1}  #pixels is i-1 because BINS is starting from one
  
      return keys
  
  
  
  
  
  
  
  if __name__ == "__main__":
  
      file_name = 'fish.jpg'
      BINS, counts, pixels, bw_data, total_counts =load_image(file_name)
      keys =within_class_variance()
      print(keys['pixel'])
      otsu_img = np.copy(bw_data).astype('uint8')
      otsu_img[otsu_img &gt; keys['pixel']]=1
      otsu_img[otsu_img &lt; keys['pixel']]=0
      #print(otsu_img.dtype)
      plt.imshow(otsu_img)
      plt.savefig('otsu.png')
      plt.show()
  </code></pre>
  
  <p>Resulting otsu image looks like this:</p>
  
  <p><a href="https://i.stack.imgur.com/0lDKT.png" rel="noreferrer"><img src="https://i.stack.imgur.com/0lDKT.png" alt="enter image description here"></a></p>
  
  <p>Here is the fish image (It has a shirtless guy holding a fish so may not be safe for work):</p>
  
  <p>Link :  <a href="https://i.stack.imgur.com/EDTem.jpg" rel="noreferrer">https://i.stack.imgur.com/EDTem.jpg</a></p>
  
  <p>EDIT:</p>
  
  <p>It turns out that by changing the threshold to 255 (The differences are more pronounced)</p>
  
  <p><a href="https://i.stack.imgur.com/2IYZ0.png" rel="noreferrer"><img src="https://i.stack.imgur.com/2IYZ0.png" alt="enter image description here"></a></p>