如何使用 OpenCV 计算类似 skimage 的图像直方图?

How to calculate skimage-like histogram of image using OpenCV?

在之前创建的代码中,直方图是使用 skimage 模块计算的,计算方式如下:

h = skimage.exposure.histogram(img, nbins=256)

但是,现在,由于某些原因,我不能使用 skimage,只能使用 OpenCV。
所以,当我尝试

h = cv2.calcHist(img, [0], None, [256], [0,256])

skimageh 和 OpenCV 的 h 的输出不同,此后的代码中断。

那么,我应该怎么做才能使 OpenCV 的直方图输出与以前相同?

示例图片:

(请点开图片看清楚,就是加了噪点的绿色)

然后我将其转换为 black-and-white 使用:

img = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
img = img/255              #to convert values from [0, 255] to [0, 1]
img = img[:, :, 0]

skimage 的直方图:

(array([  4,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         3,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
        15,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,  23,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,  49,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,  94,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0, 152,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0, 183,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0, 207,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0, 200,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0, 187,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0, 165,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0, 141,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,  78,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,  58,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,  39,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   8,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   4,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   1], dtype=int64), array([0.08249847, 0.08278952, 0.08308058, 0.08337163, 0.08366268,
       0.08395374, 0.08424479, 0.08453585, 0.0848269 , 0.08511795,
       0.08540901, 0.08570006, 0.08599112, 0.08628217, 0.08657322,
       0.08686428, 0.08715533, 0.08744638, 0.08773744, 0.08802849,
       0.08831955, 0.0886106 , 0.08890165, 0.08919271, 0.08948376,
       0.08977482, 0.09006587, 0.09035692, 0.09064798, 0.09093903,
       0.09123009, 0.09152114, 0.09181219, 0.09210325, 0.0923943 ,
       0.09268536, 0.09297641, 0.09326746, 0.09355852, 0.09384957,
       0.09414063, 0.09443168, 0.09472273, 0.09501379, 0.09530484,
       0.09559589, 0.09588695, 0.096178  , 0.09646906, 0.09676011,
       0.09705116, 0.09734222, 0.09763327, 0.09792433, 0.09821538,
       0.09850643, 0.09879749, 0.09908854, 0.0993796 , 0.09967065,
       0.0999617 , 0.10025276, 0.10054381, 0.10083487, 0.10112592,
       0.10141697, 0.10170803, 0.10199908, 0.10229013, 0.10258119,
       0.10287224, 0.1031633 , 0.10345435, 0.1037454 , 0.10403646,
       0.10432751, 0.10461857, 0.10490962, 0.10520067, 0.10549173,
       0.10578278, 0.10607384, 0.10636489, 0.10665594, 0.106947  ,
       0.10723805, 0.10752911, 0.10782016, 0.10811121, 0.10840227,
       0.10869332, 0.10898437, 0.10927543, 0.10956648, 0.10985754,
       0.11014859, 0.11043964, 0.1107307 , 0.11102175, 0.11131281,
       0.11160386, 0.11189491, 0.11218597, 0.11247702, 0.11276808,
       0.11305913, 0.11335018, 0.11364124, 0.11393229, 0.11422335,
       0.1145144 , 0.11480545, 0.11509651, 0.11538756, 0.11567862,
       0.11596967, 0.11626072, 0.11655178, 0.11684283, 0.11713388,
       0.11742494, 0.11771599, 0.11800705, 0.1182981 , 0.11858915,
       0.11888021, 0.11917126, 0.11946232, 0.11975337, 0.12004442,
       0.12033548, 0.12062653, 0.12091759, 0.12120864, 0.12149969,
       0.12179075, 0.1220818 , 0.12237286, 0.12266391, 0.12295496,
       0.12324602, 0.12353707, 0.12382812, 0.12411918, 0.12441023,
       0.12470129, 0.12499234, 0.12528339, 0.12557445, 0.1258655 ,
       0.12615656, 0.12644761, 0.12673866, 0.12702972, 0.12732077,
       0.12761183, 0.12790288, 0.12819393, 0.12848499, 0.12877604,
       0.1290671 , 0.12935815, 0.1296492 , 0.12994026, 0.13023131,
       0.13052237, 0.13081342, 0.13110447, 0.13139553, 0.13168658,
       0.13197763, 0.13226869, 0.13255974, 0.1328508 , 0.13314185,
       0.1334329 , 0.13372396, 0.13401501, 0.13430607, 0.13459712,
       0.13488817, 0.13517923, 0.13547028, 0.13576134, 0.13605239,
       0.13634344, 0.1366345 , 0.13692555, 0.13721661, 0.13750766,
       0.13779871, 0.13808977, 0.13838082, 0.13867188, 0.13896293,
       0.13925398, 0.13954504, 0.13983609, 0.14012714, 0.1404182 ,
       0.14070925, 0.14100031, 0.14129136, 0.14158241, 0.14187347,
       0.14216452, 0.14245558, 0.14274663, 0.14303768, 0.14332874,
       0.14361979, 0.14391085, 0.1442019 , 0.14449295, 0.14478401,
       0.14507506, 0.14536612, 0.14565717, 0.14594822, 0.14623928,
       0.14653033, 0.14682138, 0.14711244, 0.14740349, 0.14769455,
       0.1479856 , 0.14827665, 0.14856771, 0.14885876, 0.14914982,
       0.14944087, 0.14973192, 0.15002298, 0.15031403, 0.15060509,
       0.15089614, 0.15118719, 0.15147825, 0.1517693 , 0.15206036,
       0.15235141, 0.15264246, 0.15293352, 0.15322457, 0.15351562,
       0.15380668, 0.15409773, 0.15438879, 0.15467984, 0.15497089,
       0.15526195, 0.155553  , 0.15584406, 0.15613511, 0.15642616,
       0.15671722]))

OpenCV 的直方图:

array([[ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 1.],
   [ 0.],
   [ 2.],
   [ 0.],
   [ 7.],
   [ 8.],
   [13.],
   [12.],
   [20.],
   [20.],
   [17.],
   [ 5.],
   [ 8.],
   [ 4.],
   [10.],
   [ 3.],
   [20.],
   [ 2.],
   [18.],
   [11.],
   [39.],
   [12.],
   [44.],
   [13.],
   [23.],
   [ 8.],
   [26.],
   [ 8.],
   [12.],
   [ 1.],
   [ 2.],
   [ 0.],
   [ 2.],
   [ 0.],
   [ 1.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.],
   [ 0.]], dtype=float32)

恐怕这里有很多问题。为了重建您的结果,我使用了这段代码:

import cv2
import skimage.exposure

# Read image as-is
img = cv2.imread('TjFAo.png')

# Histogram OpenCV - incorrect usage
h_ocv = cv2.calcHist(img, [0], None, [256], [0, 256])
print(h_ocv.T)
# [[ 0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  2.  0.  7.  8. 13. 12. 20. 20.
#   17.  5.  8.  4. 10.  3. 20.  2. 18. 11. 39. 12. 44. 13. 23.  8. 26.  8.
# ...

# Some image manipulation - why? whatfor?
img = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
img = img / 255
img = img[:, :, 0]

# Histogram skimage
h_ski = skimage.exposure.histogram(img, nbins=256)
print(h_ski[0])
# [  4   0   0   0   0   0   0   0   0   0   0   0   0   3   0   0   0   0
#    0   0   0   0   0   0   0   0  15   0   0   0   0   0   0   0   0   0
# ...
  • 您在原始图像的蓝色通道上计算 OpenCV 直方图。或者,准确的说,你其实并没有那么做,因为这里的用法是不正确的。您也需要将 img 放在方括号中。
  • 您将图像转换为 YUV 颜色 space,然后为 skimage 直方图取第一个通道。为什么?你有什么想法?
  • 您不需要在此处为​​ skimage 强制执行 [0.0 ... 1.0] 范围内的值。

来看看正确的用法,先看看原图的蓝色通道:

import cv2
import skimage.exposure

# Read image as-is
img = cv2.imread('TjFAo.png')

# Histogram OpenCV - brackets around the image!
h_ocv = cv2.calcHist([img], [0], None, [256], [0, 256])
print(h_ocv.T)
# [[  0.   0.   0.   0.   0.   0.   0.   0.   1.   2.   9.  10.  27.  62.
#   107. 154. 174. 208. 213. 193. 166. 134.  73.  52.  20.   4.   1.   1.
# ...

# Histogram skimage - set source_range!
h_ski = skimage.exposure.histogram(img[..., 0], nbins=256, source_range='dtype')
print(h_ski[0])
# [  0   0   0   0   0   0   0   0   1   2   9  10  27  62 107 154 174 208
#  213 193 166 134  73  52  20   4   1   1   1   0   0   0   0   0   0   0
# ...

如果你真的想要灰度图像的直方图,首先转换图像:

import cv2
import skimage.exposure

# Read image as-is
img = cv2.imread('TjFAo.png')

# Convert to grayscale
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Histogram OpenCV - brackets around the image!
h_ocv = cv2.calcHist([img], [0], None, [256], [0, 256])
print(h_ocv.T)
# [[  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
#     0.   0.   0.   0.   0.   0.   0.   4.   3.  15.  23.  49.  94. 152.
# ...

# Histogram skimage - set source_range!
h_ski = skimage.exposure.histogram(img, nbins=256, source_range='dtype')
print(h_ski[0])
# [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
#    0   0   0   4   3  15  23  49  94 152 183 207 200 187 165 141  78  58
# ...
----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
PyCharm:       2021.1.1
OpenCV:        4.5.1
scikit-image:  0.18.1
----------------------------------------

注意skimage.exposure.histogram(img, nbins=256) returns一个有两个数组的元组,第一个是给定img中那个特定值的像素总数,第二个数组是标准化为 1.
所以,如果你只想找到给定值的像素总数,那么你可以使用这个:

def histogram(img):
    img = (img*255).astype(int)
    a = [0 for x in range(256)]
    for x in range(256):
        a[x] = np.count_nonzero(img == x)
    return a