15

I was just trying to draw histogram using new OpenCV Python interface ( cv2 ).

Below is the code i tried:

import cv2
import numpy as np
import time

img = cv2.imread('zzz.jpg')
h = np.zeros((300,256,3))
b,g,r = cv2.split(img)
bins = np.arange(256).reshape(256,1)
color = [ (255,0,0),(0,255,0),(0,0,255) ]

for item,col in zip([b,g,r],color):
    hist_item = cv2.calcHist([item],[0],None,[256],[0,255])
    cv2.normalize(hist_item,hist_item,0,255,cv2.NORM_MINMAX)
    hist=np.int32(np.around(hist_item))
    pts = np.column_stack((bins,hist))
    cv2.polylines(h,[pts],False,col)

h=np.flipud(h)

cv2.imshow('colorhist',h)
cv2.waitKey(0)

And it works fine. Below is the resulting histogram i obtained.

enter image description here


Then i modified the code a little bit.

ie changed the sixth line in code b,g,r = cv2.split(img) to b,g,r = img[:,:,0], img[:,:,1], img[:,:,2] (because it works a little faster than cv2.split).

Now the output is something different. Below is the output.

enter image description here


I checked the values of b,g,r from both the codes. They are same.

Difference lies in the output of cv2.calcHist. Result of hist_item is different in both the cases.

Question:

How does it happen? Why the result of cv2.calcHist is different when inputs are same?

EDIT

I tried a different code. Now, a numpy version of my first code.

import cv2
import numpy as np

img = cv2.imread('zzz.jpg')
h = np.zeros((300,256,3))
b,g,r = img[:,:,0],img[:,:,1],img[:,:,2]
bins = np.arange(257)
bin = bins[0:-1]
color = [ (255,0,0),(0,255,0),(0,0,255) ]

for item,col in zip([b,g,r],color):
    N,bins = np.histogram(item,bins)
    v=N.max()
    N = np.int32(np.around((N*255)/v))
    N=N.reshape(256,1)
    pts = np.column_stack((bin,N))
    cv2.polylines(h,[pts],False,col,2)

h=np.flipud(h)

cv2.imshow('img',h)
cv2.waitKey(0)

And the output is same as first one.

enter image description here

You can get my original image here: zzz.jpg

Thank you.

Abid Rahman K
  • 51,886
  • 31
  • 146
  • 157

1 Answers1

14

You should copy the array:

b,g,r = img[:,:,0].copy(), img[:,:,1].copy(), img[:,:,2].copy()

But, since calcHist() can accept channels parameter, you need not to split your img to three array.

import cv2
import numpy as np

img = cv2.imread('zzzyj.jpg')
h = np.zeros((300,256,3))

bins = np.arange(256).reshape(256,1)
color = [ (255,0,0),(0,255,0),(0,0,255) ]

for ch, col in enumerate(color):
    hist_item = cv2.calcHist([img],[ch],None,[256],[0,255])
    cv2.normalize(hist_item,hist_item,0,255,cv2.NORM_MINMAX)
    hist=np.int32(np.around(hist_item))
    pts = np.column_stack((bins,hist))
    cv2.polylines(h,[pts],False,col)

h=np.flipud(h)

cv2.imshow('colorhist',h)
cv2.waitKey(0)
HYRY
  • 94,853
  • 25
  • 187
  • 187
  • What is the need for `copy`? And what is meant by calcHist() accept channel parameter? What does it actually denote? – Abid Rahman K Feb 22 '12 at 08:00
  • 1
    you can call `cv2.calcHist([img],[CH],None,[256],[0,255])` to calculate the histogram of channel CH of img, that is img[:, :, CH]. You need to copy the array because data in img[:, :, 0] is not continuous. – HYRY Feb 22 '12 at 08:04
  • you mean c_contiguous? What is its significance? – Abid Rahman K Feb 22 '12 at 08:20
  • numpy is more efficent at manipulating arrays and is used in cv2. the python split operator doesn't keep all the data together. the copy operator collects them neatly. – Neon22 Mar 30 '12 at 01:08
  • but is it me or the image size is not 300 * 256? – user601836 Sep 10 '12 at 14:14
  • In the end, it's really the task of OpenCV's Python wrapper to make sure that the array is C-contiguous, by calling `np.ascontiguousarray`. Just my 2 cents.. – letmaik Jan 26 '14 at 20:06
  • @user601836 I have the same problems – dorbodwolf Jul 25 '18 at 07:13