superhero
superhero

Reputation: 305

Python map pixel value to it's histogram bin with cv2

Given an image, is there a quick way to map the pixel value to its bin?

img = cv2.imread('test_image.jpg')
hist_g = cv2.calcHist([img[x:x+w,y:y+h,:]], [1], None, [9], [0, 256])

This returns a 9x1 array with the counts of pixels that fall into 9 bins ranging from 0 - 256. I think.

What I want is [x:x+w,y:y+h] matrix with each entry having the bin number that the pixel gets mapped to. How can I do this?

For example, let's say I have the matrix

x = np.array([[154, 192],[67,115]])

I would like to return the matrix

x_histcounts = np.array([[5, 7],[3,4]])

based on cv2.calcHist([img[x:x+w,y:y+h,:]], [1], None, [9], [0, 256])

since 154 is in the 5th bin, 192 in the 7th bin, etc.

Upvotes: 0

Views: 748

Answers (1)

furas
furas

Reputation: 143056

If you want map pixel to 9 bins then you can convert to grayscale and later divide by (256/9) using // to get integer numbers

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

result = img_gray[x:x+w,y:y+h] // (256/9)

In calcHist you use list of channels [1] so you want histogram only for one channel and it means you don't have to convert to grayscale but use [..., ..., 1]

result = img[x:x+w, y:y+h, 1] // (256/9)

EDIT: I tested your example data [[154, 192], [67, 115]] and it gives me [[5, 6], [2, 4]] instead of [[5, 7], [3, 4]]

import numpy as np

bins_number = 9

x = np.array([[154, 192], [67, 115]])
result = (x // (256/bins_number)).astype(int)

print('result:', result.tolist())

Using Google "numpy histcounts matlab" I found also How do i replicate this matlab function in numpy? which uses np.digitize() to replicate histcounts and it also gives me [[5, 6], [2, 4]] instead of [[5, 7], [3, 4]] but I don't know if I correctly create bins ranges.

import numpy as np

bins_number = 9

x = np.array([[154, 192], [67, 115]])

bins = [(256/bins_number)*x for x in range(1, bins_number+1)]
result = np.digitize(x, bins)

print('result:', result.tolist())
print('bins:', bins)

I don't have Matlab so I tried to use histc() in Octave

>> [a, b] = histc([154, 192, 67, 115], [ 28.44444444,  56.88888889,  
85.33333333, 113.77777778, 142.22222222, 170.66666667, 199.11111111, 227.55555556, 256. ])

a =

   0   1   0   1   1   1   0   0   0

b =

   5   6   2   4

and it also gives me [[5, 6], [2, 4]] instead of [[5, 7], [3, 4]]


EDIT: I found numpy.histogram_bin_edges to generate bins ranges

import numpy as np

bins_number = 9

x = np.array([[154, 192], [67, 115], [0,1]])

bins = np.histogram_bin_edges(x, bins=9, range=(0, 256))

print('bins:', bins)

But it adds 0 as first edge so later it uses numbers 1-9 instead of 0-8 but if you use bins[1:] then it still use numbers 0-8

import numpy as np

bins_number = 9

x = np.array([[154, 192], [67, 115]])

bins = np.histogram_bin_edges(x, bins=9, range=(0, 255))
print('bins:', bins)

print('result:', np.digitize(x, bins[1:]).tolist())

Upvotes: 1

Related Questions