前文传送门:

「Python 图像处理 OpenCV (1):入门」

「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」

「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」

「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」

「Python 图像处理 OpenCV (5):图像的几何变换」

「Python 图像处理 OpenCV (6):图像的阈值处理」

「Python 图像处理 OpenCV (7):图像平滑(滤波)处理」

「Python 图像处理 OpenCV (8):图像腐蚀与图像膨胀」

「Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算」

「Python 图像处理 OpenCV (10):图像处理形态学之顶帽运算与黑帽运算」

「Python 图像处理 OpenCV (11):Canny 算子边缘检测技术」

「Python 图像处理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子边缘检测技术」

「Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术」

「Python 图像处理 OpenCV (14):图像金字塔」

「Python 图像处理 OpenCV (15):图像轮廓」

直方图

首先,第一个问题是什么是直方图?

直方图这个应该都知道吧,不知道的话就是下面这玩意:

那么图像灰度直方图是什么鬼?

直方图是都是由横纵坐标组成的,而图像直方图的横坐标 X 轴上表示的是像素值(不总是从 0 到 255 的范围),在纵坐标 Y 轴上表示的相应像素数。

所以,直方图是可以对整幅图的灰度分布进行整体了解的图示,通过直方图我们可以对图像的对比度、亮度和灰度分布等有一个直观了解。

还没看懂?简单地说,就是把一幅图像中每一个像素出现的次数都先统计出来,然后把每一个像素出现的次数除以总的像素个数,得到的就是这个像素出现的频率,然后再把像素与该像素出现的频率用图表示出来,就是灰度直方图。

上面这张图来自官方网站,在这张图中,我们可以得到如下信息:

  • 左侧区域显示图像中较暗像素的数量(左侧的灰度级更趋近于 0 )。
  • 右侧区域则显示明亮像素的数量(右侧的灰度级更趋近于 255)。
  • 暗区域多于亮区域,而中间调的数量(中间值的像素值,例如127附近)则非常少。

绘制直方图

在绘制直方图的时候,有两种方法:

  1. 使用 Matplotlib 绘图功能。
  2. 使用 OpenCV 绘图功能。

使用 Matplotlib 绘图

Matplotlib 带有一个强大的直方图绘图功能:matplotlib.pyplot.hist() ,这个方法可以直接找到直方图进行绘制。

在看示例代码之前,有两个参数需要先介绍下:

  • 数据源:数据源必须是一维数组,通常需要通过函数 ravel() 拉直图像,而函数 ravel() 的作用是将多维数组降为一维数组。
  • 像素级:一般是 256 ,表示 [0, 255] 。

代码实现:

import cv2 as cv
import matplotlib.pyplot as plt img = cv.imread("maliao.jpg") cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows() plt.hist(img.ravel(), 256, [0, 256])
plt.show()

输出结果:

当然,我们除了可以绘制灰度直方图以外,还可以绘制出 r,g,b 不同通道的直方图,可以看下面的代码:

import cv2 as cv
import matplotlib.pyplot as plt img = cv.imread("tiankong.jpg")
color = ('b', 'g', 'r') cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows() 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 绘制直方图

使用 OpenCV 绘制直方图还是有点费劲儿的,首先我们确认横坐标是图像中各个像素点的灰度级,纵坐标是具有该灰度级的像素个数。

接下来要介绍几个新概念:

BINS:

在前面的直方图中,我们显示的是每个像素值的像素数,即从 0 到 255 。那么现在会有一个问题,如果一个直方图我并不想找到所有的像素数量,而是取一定范围内的像素值,如:先找到 0 到 15 之间的像素数,然后找到 16 到 31 之间,......, 240 到 255 之间的像素数。

这样,我们将这个直方图分成了 16 个子部分,每个子部分的值就是其中所有像素数的总和。每个子部分都称为 BIN 。在第一种情况下, BIN 的数量为 256 个(每个像素一个),而在第二种情况下, BIN 的数量仅为 16 个。

DIMS:

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

RANGE:

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

使用 OpenCV 的绘制直方图,我们会用到一个新的函数 calcHist() ,它的原函数如下:

def calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None):
  • 参数1:要计算的原图,以方括号的传入,如:[img]。
  • 参数2:灰度图写[0]就行,彩色图 B/G/R 分别传入 [0]/[1]/[2] 。
  • 参数3:要计算的区域ROI,计算整幅图的话,写None。
  • 参数4:就是我们上面提到的 BINS ,子区段数目。
  • 参数5:range,要计算的像素值范围,一般为 [0,256] 。

接下来我们开始画图,首先我们需要使用 calcHist() 来查找整个图像的直方图。

import cv2 as cv
import matplotlib.pyplot as plt img = cv.imread("tiankong.jpg")
# 参数:原图像 通道[0]-B 掩码 BINS为256 像素范围0-255
histB = cv.calcHist([img], [0], None, [256], [0, 255])
histG = cv.calcHist([img], [1], None, [256], [0, 255])
histR = cv.calcHist([img], [2], None, [256], [0, 255]) cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows() plt.plot(histB, color='b')
plt.plot(histG, color='g')
plt.plot(histR, color='r')
plt.show()

直方图均衡化

一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。

  • 灰度图均衡,直接使用 equalizeHist() 函数。
  • 彩色图均衡,分别在不同的通道均衡后合并。

示例代码如下:

import cv2 as cv
import numpy as np img = cv.imread("dahai.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 灰度图均衡化
equ = cv.equalizeHist(gray)
# 水平拼接原图和均衡图
result1 = np.hstack((gray, equ))
cv.imwrite('grey_equ.png', result1) # 彩色图像均衡化,需要分解通道 对每一个通道均衡化
(b, g, r) = cv.split(img)
bH = cv.equalizeHist(b)
gH = cv.equalizeHist(g)
rH = cv.equalizeHist(r)
# 合并每一个通道
equ2 = cv.merge((bH, gH, rH))
# 水平拼接原图和均衡图
result2 = np.hstack((img, equ2))
cv.imwrite('bgr_equ.png', result2)

结果:



自适应直方图均衡

上面介绍的直方图均值化是针对整幅图片的,这样有好处也有不好的地方,会导致一些图片部位太亮,导致大部分细节丢失。如下面这两张图片:

直方图均衡后,背景对比度确实得到了改善。但是在两个图像中比较雕像的脸,由于亮度过高,丢失了大多数信息。

因此,为了解决这个问题,引入了 自适应直方图均衡 来解决这个问题。

它在每一个小区域内(默认 8×8 )进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制。

import cv2 as cv
import numpy as np img = cv.imread('clahe_src.jpg', 0) # 全局直方图均衡
equ = cv.equalizeHist(img) # 自适应直方图均衡
clahe = cv.createCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))
cl1 = clahe.apply(img) # 水平拼接三张图像
result1 = np.hstack((img, equ, cl1)) cv.imwrite('clahe_result.jpg', result1)

参考

https://blog.csdn.net/Eastmount/article/details/83758402

http://www.woshicver.com/FifthSection/4_10_1_直方图-1:查找,绘制,分析/

https://zhuanlan.zhihu.com/p/61879400

Python 图像处理 OpenCV (16):图像直方图的更多相关文章

  1. Python 图像处理 OpenCV (14):图像金字塔

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  2. Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 图像属性 图像 ...

  3. Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  4. Python 图像处理 OpenCV (5):图像的几何变换

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  5. Python 图像处理 OpenCV (6):图像的阈值处理

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  6. Python 图像处理 OpenCV (15):图像轮廓

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  7. Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 普通操作 1. 读取像素 读取像素可以通过行坐标和列坐标来进行访问,灰度图像直接返回灰度值,彩色图像则返回B.G.R三个分量. 需 ...

  8. Python 图像处理 OpenCV (7):图像平滑(滤波)处理

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  9. Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

随机推荐

  1. 什么?你还不会通过纯js提交表单?

    如果程序已经封装好了, 不管后台是java .asp.net   .还是php   ?这个时候你的客户突然追加说我要 追加表单验证?   what  妇产科    怎么办? submit  自带刷新效 ...

  2. windows dos 批量重命名文件

    描述 在工作中经常出现 在同一目录下有一些 很多相同扩展名的文件但是名字看起来很乱各不同,我们想将它们统一重命名一下统一的格式,如果一个个去改名字太麻烦了. 这里我门就可以使用windows下 dos ...

  3. 一篇夯实一个知识点系列--python装饰器

    写在前面 本系列目的:希望可以通过一篇文章,不望鞭辟入里,但求在工程应用中得心应手. 装饰器模式是鼎鼎大名的23种设计模式之一.装饰器模式可以在不改变原有代码结构的情况下,扩展代码功能. Python ...

  4. shell专题(三):Shell脚本入门

    1.脚本格式 脚本以#!/bin/bash开头(指定解析器) 2.第一个Shell脚本:helloworld (1)需求:创建一个Shell脚本,输出helloworld (2)案例实操: [atgu ...

  5. 李航统计学习方法(第二版)(六):k 近邻算法实现(kd树(kd tree)方法)

    1. kd树简介 构造kd树的方法如下:构造根结点,使根结点对应于k维空间中包含所有实例点的超矩形区域;通过下面的递归方法,不断地对k维空间进行切分,生成子结点.在超矩形区域(结点)上选择一个坐标轴和 ...

  6. python 装饰器(五):装饰器实例(二)类装饰器(类装饰器装饰函数)

    回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文). 那么用类来实现也是也可以的.我们可以让类的构造函数__init__()接受一个函 ...

  7. Windows 磁盘分区后如何再合并&如何用Windows自带工具扩大某个分区

    Windows 磁盘分区后如何再合并&用Windows自带工具扩大某个分区 注:此方法有一定的成功率,更加完善可行的方法请看http://www.diskgenius.cn/help/part ...

  8. IDEA JRebel热部署( IDEA版本是2020.1.2)

    1.安装JRebel插件 在IDEA->Settings->plugins先安装JRebel插件: 2.下载工具 安装好JRebel后,找到lanyus大神文章中写的git地址:http: ...

  9. 用前端姿势玩docker【一】Docker通俗理解常用功能汇总与操作埋坑

    前言 首先一句话表达个人对docker的理解:与传统虚拟技术基于硬件及物理资源的虚拟化相比,Docker更加轻量化,docker为基于操作系统或内核级别的虚拟化,并且提供了从各种机制与操作以满足从开发 ...

  10. 少儿编程:python趣味编程第二课,如何在pygame中写文字

    python趣味编程第二课:本文仅针对8-16岁的青少年,所以流程是按如何去教好中小学生走的,并不适合成人找工作学习,因为进度也是按照青少年走的 大家好,我是C大叔,上一篇文章已经跟大家介绍了一款开发 ...