Create Image Histogram Manually and Efficiently in Python
<p>I want to write codes that can show the histogram of an image without using built in Matplotlib hist function.</p>

<p>Here is my codes:</p>

<pre><code>import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

def manHist(img):
   row, col = img.shape # img is a grayscale image
   y = np.zeros((256), np.uint64)
   for i in range(0,row):
      for j in range(0,col):
         y[img[i,j]] += 1
   x = np.arange(0,256)
   plt.bar(x,y,color="gray",align="center")
   plt.show()

def main():
   img = cv.imread("C:/Users/Kadek/Documents/MATLAB/2GS.jpg")
   manHist(img)

main()
</code></pre>

<p>My question is, is there a more efficent way to make an array of pixel value frequency without using for loop?</p>

<p>A NumPy based vectorized solution would be with <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html" rel="nofollow"><code>np.bincount</code></a> -</p>

<pre><code>out = np.bincount(img.ravel(),minlength=256)
</code></pre>

<p>Another vectorized approach based on <a href="http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.sum.html" rel="nofollow"><code>.sum()</code></a> -</p>

<pre><code>out = (img.ravel() == np.arange(256)[:,None]).sum(1)
</code></pre>

<p>Sample run to verify results -</p>

<pre><code>In [155]: # Input image (512x512) as array
     ...: img = np.random.randint(0,255,(512,512))
     ...: 
     ...: # Original code
     ...: row, col = img.shape
     ...: y = np.zeros((256), np.uint64)
     ...: for i in range(0,row):
     ...:     for j in range(0,col):
     ...:         y[img[i,j]] += 1
     ...:         

In [156]: out1 = np.bincount(img.ravel(),minlength=256)

In [157]: out2 = (img.ravel() == np.arange(256)[:,None]).sum(1)

In [158]: np.allclose(y,out1)
Out[158]: True

In [159]: np.allclose(y,out2)
Out[159]: True
</code></pre>

python opencv numpy image-processing matplotlib