Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。
基础知识铺垫
截止到本篇博客,已经第二次听到直方图这个概念了,有必要将其搞懂。
图像直方图(histogram)是图像统计学特征,用来统计像素值出现的频次,常用在分析图像的基本特征。
创建直方图一般分为两个步骤:
统计数据
绘制直方图
直方图的定义
横坐标:图像中各个像素点的灰度级
纵坐标:该灰度级的像素个数
绘制直方图需要 matplotlib 库,这个需要自行安装一下。
matplotlib 中 pyplot 绘制直方图
在 pyplot 中提供了一个绘制直方图的函数,名称为 hist。
函数原型介绍
matplotlib.pyplot.hist() 函数原型如下
( n, bins, patches) = matplotlib. pyplot. hist( x, bins= None , range = None , density= None , weights= None ,
cumulative= False , bottom= None , histtype= 'bar' , align= 'mid' , orientation= 'vertical' ,
rwidth= None , log= False , color= None , label= None , stacked= False , normed= None , * , data= None , ** kwargs)
参数非常多,实际应用中只需掌握几个重要参数。
最简单的测试代码如下:
import numpy as np
import matplotlib. pyplot as plt
data = np. random. normal( 0 , 1 , 10000 )
n, bins, patches = plt. hist( data)
plt. show( )
运行效果如下:
其中,np.random.normal(0,1,10000) 函数说明如下,np.random.normal() 是一个正态分布,normal这里是正态的意思。
该函数原型为:
numpy. random. normal( loc= 0.0 , scale= 1.0 , size= None )
参数说明如下:
loc:概率分布的均值,对应着整个分布的中心 center
scale:概率分布的标准差,对应于分布的宽度,scale 越大,越矮胖,scale 越小,越瘦高
size:数据类型为 int or tuple of ints, 输出的 shape,默认为 None,只输出一个值
其实该函数的目的就是,输出为高斯分布的一组数或一个值。 简单案例:
data = np. random. normal( loc= 0 , scale= 1 , size= 2 )
print ( data)
继续回顾 matplotlib.pyplot.hist() 函数的相关参数(官网说明 ):
只选取其中比较重要的几个参数如下:
x:(n,) array or sequence of (n,) arrays 指定要绘制直方图的数据,必须是一维数组 .使用.ravel()将你的通道值转为一维数组
bins:integer or sequence or ‘auto’, optional 指定直方图条形的个数,integer 或 auto,也可以不设置.举例[1,2,3,4],则第一个柱为取值[1,2),一次类推,最后一个是取值[3,4].默认 taken from the rcParam hist.bins.
range:tuple or None, optional 数组或者不给.给出数组将指定直方图数据的上下界,超出范围的舍弃.不设置的话包含绘图数据的最大值和最小值;默认为 None
基于上述内容,将一副图像的直方图显示出来。
做一些准备工作
x: 图像,必须是一维数组
其中函数 ravel b = a.ravel() 功能: 将多维数组降为一维数组 格式: 一维数组=多维数组.revel()
bins: 一般是 256,指[0, 255]
以上内容掌握之后,就可以处理图像的直方图了,代码如下:
import cv2
from matplotlib import pyplot as plt
def plot_demo ( image) :
plt. hist( image. ravel( ) , 256 , [ 0 , 256 ] )
plt. show( )
if __name__ == "__main__" :
img = cv2. imread( "./106.jpg" )
cv2. namedWindow( "input image" , cv2. WINDOW_AUTOSIZE)
cv2. imshow( "input image" , img)
plot_demo( img)
cv2. waitKey( 0 )
cv2. destroyAllWindows( )
python opencv 直方图(histogram)
函数原型介绍
在 Python OpenCV 中实现直方图的函数为cv2.calcHist,原型如下:
cv2. calcHist( img, channels, mask, histSize, ranges[ , hist[ , accumulate ] ] )
参数说明:
img:图像,方括号方式传入,即[img];
channels:选取图像的哪个通道,用方括号给出的,计算直方图的 channel 的索引,如果输入时灰度图,值就是 [0],对于彩色图片,你可以传 [0] ,[1] 和 [2] 来分别计算蓝色,绿色和红色通道的直方图;
mask:掩膜,如果要找整个图像的直方图,这里传入"None"。如果你想找到特定区域图片的直方图,需要使用掩膜,只计算值>0 的位置上像素的颜色直方图
histSize:直方图大小,BINS 数量(BINS 是啥,下面细说),要方括号传入,对于全刻度,传入 [256]
ranges:直方图范围,一般来说是[0,256]
关于上文提及的 BINS 等内容涉及直方图如下概念:
BINS: 在上面的直方图当中,如果像素值是 0 到 255,则需要 256 个值来显示直方图。 但是,如果不需要知道每个像素值的像素数目,只想知道两个像素值范围内的像素点数目即可?首先像素值在 0--15 之间的像素点数目,然后是 16--31 ……直到 240--255,即每次间隔 16 个数字,将 256 个值分成 16 份,每份计算综合。每个分成的小组就是一个 BIN(箱)。在 opencv 中使用 histSize 表示 BINS。
彩色图像,不同通道的直方图
首先绘制蓝色通道的直方图,代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv. imread( './106.jpg' )
hist = cv. calcHist( [ img] , [ 0 ] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist, label= 'B' , color= 'b' )
plt. show( )
运行结果如下: 三个通道同时绘制代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv. imread( './106.jpg' )
color = ( 'b' , 'g' , 'r' )
for i, col in enumerate ( color) :
hist = cv. calcHist( [ img] , [ i] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist, color= col)
plt. xlim( [ 0 , 256 ] )
plt. show( )
BGR 直方图如下:
直方图均衡化 (Histogram Equalization)
如果图像的灰度分布不均匀,集中在一个比较窄的范围内,这样图像的细节就会不清晰,对比度低。
这种情况可以使用直方图均衡化,对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等。
执行之后,直方图中间峰值部分对比度增强,两侧谷底部分对比度降低。图像的灰度范围拉伸之后,灰度均匀分布,反差增大,增强图像细节。
理论的东西就是上面那些了,实操起来才可以看到效果。
函数原型介绍
使用 cv2.equalizeHist 方法来得到直方图均衡化之后的图像,函数原型如下:
cv2. equalizeHist( src[ , dst] )
参数说明:
src:源图像。图像必须是灰度图。
dst:目标图像。
测试代码如下:
import cv2 as cv
import numpy as np
import matplotlib. pyplot as plt
image = cv. imread( "2o.jpg" )
gray = cv. cvtColor( image, cv. COLOR_BGR2GRAY)
cv. imshow( "gray" , gray)
hist = cv. calcHist( [ gray] , [ 0 ] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist)
plt. show( )
dst = cv. equalizeHist( gray)
cv. imshow( "dst" , dst)
hist = cv. calcHist( [ dst] , [ 0 ] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist)
plt. show( )
运行结果与直方图:
运行直方图均衡化之后的图像如下: 上述案例为灰度图直方图均衡化,对于彩色图像一样可以进行图像增强操作。
import cv2 as cv
import numpy as np
img = cv. imread( 'th.jpeg' )
cv. imshow( 'img' , img)
b, g, r = cv. split( img)
bH = cv. equalizeHist( b)
gH = cv. equalizeHist( g)
rH = cv. equalizeHist( r)
dst = cv. merge( ( bH, gH, rH) )
cv. imshow( 'dst' , dst)
cv. waitKey( 0 )
使用cv2.split函数分离图像的颜色通道,分别得到各个通道的直方图,再使用cv2.merge函数合并各通道,得到彩色图像的直方图均衡化。
以上提及的叫做全局直方图均衡化,下面为大家在介绍一下局部直方图均衡化。
局部直方图均衡化在有的地方也被叫做 CLAHE 有限对比适应性直方图均衡化。
大概实现过程如下: 整幅图像会被分成很多小块,这些小块被称为 “tiles”(tiles 的默认大小是 8x8),然后再对每一个小块分别进行直方图均衡化。
函数原型如下:
cv2. createCLAHE( clipLimit, tileGridSize)
参数说明:
clipLimit:对比度限制的阈值
tileGridSize:图像分割每块的尺寸,默认 8x8
运行下述代码,得到的结果可以与全局直方图均衡化做一下比较。
import cv2 as cv
import numpy as np
import matplotlib. pyplot as plt
image = cv. imread( "2o.jpg" )
gray = cv. cvtColor( image, cv. COLOR_BGR2GRAY)
cv. imshow( "gray" , gray)
hist = cv. calcHist( [ gray] , [ 0 ] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist)
plt. show( )
clahe = cv. createCLAHE( clipLimit= 2.0 , tileGridSize= ( 8 , 8 ) )
dst = clahe. apply ( gray)
cv. imshow( "dst" , dst)
hist = cv. calcHist( [ dst] , [ 0 ] , None , [ 256 ] , [ 0 , 256 ] )
plt. plot( hist)
plt. show( )
橡皮擦的小节
今天重点学习了一下直方图,写了这么多,只有一个原因,就是这是第二次碰到了,当一个知识点再次出现时,就要在进一步的学习下,因为大概率它是重点知识。
相关阅读
Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
Python 爬虫小课,精彩 9 讲
今天是持续写作的第 65 / 100 天。 如果你有想要交流的想法、技术,欢迎在评论区留言。
如果你想跟博主建立亲密关系,可以关注博主,或者关注博主公众号 “非本科程序员”,了解一个非本科程序员是如何成长的。 博主 ID:梦想橡皮擦,希望大家点赞、评论、收藏
本文同步分享在 博客“梦想橡皮擦”(CSDN)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划 ”,欢迎正在阅读的你也加入,一起分享。