opencv21:Histograms直方图-查找、绘制和分析

目标

学会

  • 使用OpenCV和Numpy函数查找直方图
  • 使用OpenCV和Matplotlib函数绘制直方图
  • 函数:cv2.calcHist()np.histogram()等。

理论

直方图(Histograms)是什么?可以将直方图视为图形或绘图,从而可以总体了解图像的强度分布。它是在X轴上具有像素值(不总是从0到255的范围),在Y轴上具有图像中相应像素数量的图

hog只是理解图像的另一种方式。通过查看图像的直方图,可以直观地了解该图像的对比度,亮度,强度分布等。当今几乎所有图像处理工具都提供直方图功能。下图来自网站( Cambridge in Color website)的图片
在这里插入图片描述

可以看到图像及其直方图。(记住,直方图是针对灰度图像而非彩色图像绘制的)。直方图的左侧区域显示图像中较暗像素的数量,而右侧区域则显示明亮像素的数量。从直方图中,可以看到暗区域多于亮区域,而中间调的数量(中间值的像素值,例如127附近)则非常少。

寻找直方图

现在有了关于直方图的认识,就可以研究如何找到它。OpenCV和Numpy都为此内置了实现函数。在使用这些函数之前,需要了解一些与直方图有关的术语。

  • BINS:上面的直方图显示每个像素值的像素数,即从0到255。即,需要256个值来显示上面的直方图。但是假设如果不需要分别找到所有像素值的像素数,而是找到像素值间隔中的像素数怎么办?
    例如,需要找到介于0到15之间的像素数,然后找到16到31之间,…,240到255之间的像素数。只需要16个值即可表示直方图。在这里插入图片描述
    因此,要做的就是将整个直方图分成16个子部分,每个子部分的值就是其中所有像素数的总和
    每个子部分都称为BIN。在第一种情况下,bin的数量为256个(每个像素一个),而在第二种情况下,bin的数量仅为16个。BINS由OpenCV文档中的histSize术语表示。

  • DIMS:为其收集数据的参数的数量。在这种情况下,仅收集关于强度值这一件事的数据,所以这里是1。

  • RANGE:要测量的强度值的范围。通常,它是[0,256],即所有强度值。

OpenCV中的直方图计算

在OpenCv中,可以使用cv2.calcHist()函数查找直方图。熟悉一下该函数及其参数:

cv2.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])

  • images:它是uint8或float32类型的源图像。它应该放在方括号中,即 [img]
  • channels:也以方括号给出。是计算直方图的通道的索引。例如,如果输入为灰度图像,则其值为[0]。对于彩色图像,可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图
  • mask:图像掩码。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特定区域的直方图,则必须为此创建一个掩码图像并将其作为掩码
  • histSize:这表示BIN计数。需要放在方括号中。对于全尺寸,设置为[256]
  • ranges:这是RANGE。通常为[0,256]
    在这里插入图片描述
img = cv.imread('home.jpg',0)
hist = cv.calcHist([img],[0],None,[256],[0,256]) 

hist是256x1的数组,每个值对应于该图像中具有相应像素值的像素数

在这里插入图片描述

Numpy的直方图计算

Numpy也提供了一个函数np.histogram()。因此,除了cv2.calcHist()函数外,可以尝试下面的代码:

import numpy as np
hist, bins = np.histogram(img.ravel(), 256, [0,256])   # 先展成一维再统计

在这里插入图片描述

在这里插入图片描述
可以看到,hist与opencv计算的结果相同。但是bin具有257个元素(0~256),因为Numpy计算出bin的范围为0-0.99、1-1.99、2-2.99等, 因此最终范围为255-255.99。为了表示这一点,他们还在最后添加了256。但使用时候不需要256, 最多255就足够了。

另外
Numpy还有另一个函数np.bincount(),它比np.histogram()快10倍左右。因此,对于一维直方图,可以更好地尝试一下。不要忘记在np.bincount中设置minlength = 256。例如,

hist = np.bincount(img.ravel(),minlength = 256)  # 先展成一维再统计

在这里插入图片描述

注意
OpenCV函数cv2.calcHist()np.histogram()快大约40倍。因此,尽可能使用OpenCV函数的cv2.calcHist()

绘制直方图

绘制直方图有两种方法,

  • 简短的方法:使用Matplotlib绘图功能
  • 稍长的方法:使用OpenCV绘图功能
使用Matplotlib

Matplotlib带有直方图绘图功能:matplotlib.pyplot.hist()
直接找到直方图并将其绘制, 无需使用calcHist()或np.histogram()函数来查找直方图。代码如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('hog.jpg', 0)
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.subplot(1,2,2)
plt.hist(img.ravel(), 256, [0,256])
plt.show()

结果如下:

在这里插入图片描述

或者,可以使用matplotlib的法线图,这对于BGR图是很好的。为此,需要首先找到直方图数据。代码如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('hog.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

结果如下:
在这里插入图片描述

可以从上图中得出,蓝色在图像中具有一些高值域(显然这应该是由于天空)

使用 OpenCV

在这里可以调整直方图的值及其bin值,使其看起来像x,y坐标,以便可以使用cv2.line()cv2.polyline()函数绘制它以生成与上述相同的图像。OpenCV-Python官方示例已经提供了样例代码

#!/usr/bin/env python
# https://github.com/opencv/opencv/blob/master/samples/python/hist.py
''' This is a sample for histogram plotting for RGB images and grayscale images for better understanding of colour distribution

Benefit : Learn how to draw histogram of images
          Get familier with cv.calcHist, cv.equalizeHist,cv.normalize and some drawing functions

Level : Beginner or Intermediate

Functions : 1) hist_curve : returns histogram of an image drawn as curves
            2) hist_lines : return histogram of an image drawn as bins ( only for grayscale images )

Usage : python hist.py <image_file>

Abid Rahman 3/14/12 debug Gary Bradski
'''

# Python 2/3 compatibility
from __future__ import print_function

import numpy as np
import cv2 as cv

bins = np.arange(256).reshape(256,1)

def hist_curve(im):
    h = np.zeros((300,256,3))
    if len(im.shape) == 2:
        color = [(255,255,255)]
    elif im.shape[2] == 3:
        color = [ (255,0,0),(0,255,0),(0,0,255) ]
    for ch, col in enumerate(color):
        hist_item = cv.calcHist([im],[ch],None,[256],[0,256])
        cv.normalize(hist_item,hist_item,0,255,cv.NORM_MINMAX)
        hist=np.int32(np.around(hist_item))
        pts = np.int32(np.column_stack((bins,hist)))
        cv.polylines(h,[pts],False,col)
    y=np.flipud(h)
    return y

def hist_lines(im):
    h = np.zeros((300,256,3))
    if len(im.shape)!=2:
        print("hist_lines applicable only for grayscale images")
        #print("so converting image to grayscale for representation"
        im = cv.cvtColor(im,cv.COLOR_BGR2GRAY)
    hist_item = cv.calcHist([im],[0],None,[256],[0,256])
    cv.normalize(hist_item,hist_item,0,255,cv.NORM_MINMAX)
    hist = np.int32(np.around(hist_item))
    for x,y in enumerate(hist):
        cv.line(h,(x,0),(x,y[0]),(255,255,255))
    y = np.flipud(h)
    return y


def main():
    import sys

    if len(sys.argv)>1:
        fname = sys.argv[1]
    else :
        fname = 'lena.jpg'
        print("usage : python hist.py <image_file>")

    im = cv.imread(cv.samples.findFile(fname))

    if im is None:
        print('Failed to load image file:', fname)
        sys.exit(1)

    gray = cv.cvtColor(im,cv.COLOR_BGR2GRAY)


    print(''' Histogram plotting \n
    Keymap :\n
    a - show histogram for color image in curve mode \n
    b - show histogram in bin mode \n
    c - show equalized histogram (always in bin mode) \n
    d - show histogram for gray image in curve mode \n
    e - show histogram for a normalized image in curve mode \n
    Esc - exit \n
    ''')

    cv.imshow('image',im)
    while True:
        k = cv.waitKey(0)
        if k == ord('a'):
            curve = hist_curve(im)
            cv.imshow('histogram',curve)
            cv.imshow('image',im)
            print('a')
        elif k == ord('b'):
            print('b')
            lines = hist_lines(im)
            cv.imshow('histogram',lines)
            cv.imshow('image',gray)
        elif k == ord('c'):
            print('c')
            equ = cv.equalizeHist(gray)
            lines = hist_lines(equ)
            cv.imshow('histogram',lines)
            cv.imshow('image',equ)
        elif k == ord('d'):
            print('d')
            curve = hist_curve(gray)
            cv.imshow('histogram',curve)
            cv.imshow('image',gray)
        elif k == ord('e'):
            print('e')
            norm = cv.normalize(gray, gray, alpha = 0,beta = 255,norm_type = cv.NORM_MINMAX)
            lines = hist_lines(norm)
            cv.imshow('histogram',lines)
            cv.imshow('image',norm)
        elif k == 27:
            print('ESC')
            cv.destroyAllWindows()
            break

    print('Done')


if __name__ == '__main__':
    print(__doc__)
    main()
    cv.destroyAllWindows()

掩码的应用

使用cv2.calcHist()能查找整个图像的直方图。如果想找到图像某些区域的直方图呢?只需创建一个掩码图像,将要找到直方图为白色,否则黑色, 然后把这个作为掩码传递。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv2.imread('hog.jpg', 0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:200, 100:200] = 255  # set white
masked_img = cv2.bitwise_and(img,img,mask = mask)
# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img], [0], None, [256],[0,256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0,256])

# plt.subplot(221) <--> plt.subplot(2,2,1)
plt.subplot(2,2,1)
plt.imshow(img, cmap='gray')
plt.subplot(2,2,2)
plt.imshow(mask, cmap='gray')
plt.subplot(2,2,3)
plt.imshow(masked_img, cmap='gray')
plt.subplot(2, 2, 4)
plt.plot(hist_full, color='r')
plt.plot(hist_mask, color='g')
plt.xlim([0, 255])
plt.show()
plt.show()

查看结果。在直方图中,蓝线表示完整图像的直方图,绿线表示掩码区域的直方图。

在这里插入图片描述

附加资源

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncle_ll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值