2

I want to write codes that can show the histogram of an image without using built in Matplotlib hist function.

Here is my codes:

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()

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

9
  • There's no other way than walking through every value in the image, though a vectorized numpy solution is going to be some orders of magnitude faster Commented Oct 16, 2016 at 17:09
  • So, you are not using the third axis of channels as you have : y[img[i,j]]? Commented Oct 16, 2016 at 17:14
  • You could use collections.Counter for this, although that might not be "manual" enough for you. Commented Oct 16, 2016 at 17:16
  • @FranciscoCouzo thank you for your advice, but i don't understand how to use vectorized numpy. Commented Oct 16, 2016 at 17:21
  • @Divakar Yes, it's a grayscaled image. Commented Oct 16, 2016 at 17:22

1 Answer 1

2

A NumPy based vectorized solution would be with np.bincount -

out = np.bincount(img.ravel(),minlength=256)

Another vectorized approach based on .sum() -

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

Sample run to verify results -

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
Sign up to request clarification or add additional context in comments.

5 Comments

both returning false in my codes. i wrote exactly same like yours.
@KadekDwiBudiUtama You haven't answered by question in the comments, whether that should be row, col = img.shape instead or not?
Oh, sorry. It's work after using only 1 channel! Thank's!
@KadekDwiBudiUtama Kindly update your code in the question with that correction.
@KadekDwiBudiUtama Took the liberty to make the edit myself. If not coherent, feel free to make additional edits.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.