直方图实例详解(颜色直方图、灰度直方图)
本篇目录:
🦄 一、前言
最近经常被人质疑年龄的问题,没错,我年龄是很大了,但我很自豪。我因故在家专心做家务很多很多年,一直和父母一起居住,连电脑都没时间碰,可以说和社会脱节了。重新拾起计算机各种知识也是从去年才开始。从前端技术,到后台编程,从。。。到。。。从UI设计到现在最新的ai绘图,我都能。。。,记录我学习过程的CSDN博客(https://blog.csdn.net/weixin_69553582?type=lately)不到一年时间,访问量马上就要到50万了(纯自然状态,我不善也不喜营销自己)
去年某个时候,我曾看到一条别人的动态(他当时被一个年龄较小的网友怼,说他年纪大)
我当时是这样回复的:
我希望你和我那么大的时候,能够做得比我更出色!
年轻是代表了无限可能,但其中也有很多变数。纵观各个年龄层次的人,能做到卓越的有几人?能做到优秀的又有几人?
只要你今天比昨天的自己进步,什么时候开始启航并不是问题。
花自开不是为了被人欣赏,
人自律自强不是为了被人称赞!
逆境清醒
2023.4.16
🦄 二、直方图的概念
在图像处理中, 经常用到直方图, 如颜色直方图、 灰度直方图等。
直方图是进行图像处理过程中的一种非常重要的工具。它是从图像内部灰度级的角度对图像进行表述。
直方图统计的是图像内各个灰度级出现的次数。
图像的构成是有像素点构成的,每个像素点的值代表着该点的颜色(灰度图或者彩色图)。
直方图就是对图像的中的这些像素点的值进行统计,得到一个统一的整体的灰度概念。
直方图的好处就在于可以清晰了解图像的整体灰度分布,便于后期依据直方图处理图像。
图像的直方图就是像素值出现的总的频数,其公式如下:
通常将其除以总的像素数,通过概率的形式进行表述:
🦄 三、颜色直方图
(1)、颜色直方图定义
颜色直方图:
- 1、颜色直方图,是一种能快速描述图像整体像素值分布的统计信息图表。
图表能显示出某一像素值范围的像素点的个数,X轴为像素值,Y为个数。
- 2、颜色直方图只能描述颜色的分布,不能描述数据几何上的信息。
即只知道这个像素值范围的点的个数有多少个,但无法知道这个点在哪个位置上多。
(2)、颜色直方图使用方法
绘制颜色直方图方法:
用numpy的histogram()函数得到直方图的信息,再用matplotlib绘制出图像。
函数:img.ravel():
作用:把多维数组转化成一维数组,使用方法见下文举例。
绘制直方图函数:matplotlib.pyplot.hist(X, BINS)
- X:数据源,必须是一维的。通常二维图像,需要使用ravel()函数将图像处理为一维数据源。
- BINS:表示灰度级的分组情况。
hist函数使用方式举例:
plt.hist(img.ravel(),256,[0,256],facecolor ='black')
- hist:hist函数只支持一维的数组(数组下标为横坐标,值为纵坐标)
- 256:256 表示横坐标的最大值为256,有256条柱
- [0,256]:[0,256]表示数据显示范围,横坐标超过256的数据也归到256处
- facecolor:facecolor 表示柱状图的颜色
函数ravel()的作用是将二维数组降维成一维数组,举例如下:
假设存在一图像img,其灰度值分别为:
[[235 198 148]
[238 203 153]
[241 207 161]
...
[250 227 205]
[255 232 210]
[255 238 216]]
我们使用ravel()函数对img图像降维处理:
matrix = img.ravel()
可得到matrix为:
[235 198 148 238 203 153 241 207 161... 250 227 205 255 232 210 255 238 216]
(3)、绘制颜色直方图
①、绘制普通颜色直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2aa.jpg') #读入图像
hist, bins = np.histogram(img.ravel(), bins=50)
#通过histogram就可以得到每段像素值范围bins的像素点个数hist
plt.hist(img.ravel(), bins=50);
cv2.imshow('img', img)
cv2.waitKey(0)
plt.show()
运行效果:
②、绘制灰度图像直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img_gray = cv2.imread(r'girl2aaa.jpg', cv2.IMREAD_GRAYSCALE) #读入灰度图像
hist, bins = np.histogram(img_gray.ravel(), bins=50)
plt.hist(img_gray.ravel(), bins=50);
cv2.waitKey(0)
plt.show()
运行效果:
③、绘制彩色图像直方图
彩色图像有三个通道,分别绘制。
可以看到每个通道上像素的分布情况,分析出原图中哪种颜色比较多。
完整代码:
import cv2
import matplotlib.pyplot as plt
# 彩色图像直方图
img_bgr_data = cv2.imread(r'girl2aaa.jpg')
plt.figure(figsize=(15, 5)) #设置画布的大小
# B通道 直方图
ax1 = plt.subplot(131)
ax1.hist(img_bgr_data[:, :, 0].ravel(), bins=50, color='b')
# G通道 直方图
ax2 = plt.subplot(132)
ax2.hist(img_bgr_data[:, :, 1].ravel(), bins=50, color='g')
# R通道 直方图
ax3 = plt.subplot(133)
ax3.hist(img_bgr_data[:, :, 2].ravel(), bins=50, color='r')
cv2.waitKey(0)
plt.show()
运行效果:
④、不同亮度图片直方图对比图
(a)、普通图像
完整代码:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2aaa.jpg')
cv2.imshow('img', img)
plt.hist(img.ravel(), 256)
plt.show()
cv2.waitKey(0)
运行效果:
| |
图像 | 图像正常 |
(b)、偏暗图像
完整代码:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2bbb.jpg')
cv2.imshow('img', img)
plt.hist(img.ravel(), 256)
plt.show()
cv2.waitKey(0)
运行效果:
| |
偏暗图像 | 图像偏暗 |
(c)、偏亮图像
完整代码:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2ccc.jpg')
cv2.imshow('img', img)
plt.hist(img.ravel(), 256)
plt.show()
cv2.waitKey(0)
运行效果:
偏亮图像 | 图像偏亮 |
(d)、亮度过于集中图像
完整代码:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2ddd.jpg')
cv2.imshow('img', img)
plt.hist(img.ravel(), 256)
plt.show()
cv2.waitKey(0)
运行效果:
图像 | 图像亮度过于集中 |
⑤、彩色图像直方图(绘制在同一张图)
代码:
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread("cat1.jpg")
n_bins = 256
hist_range = [0, 256]
hists = []
channels = {0: "blue", 1:"green", 2: "red"}
for ch in channels:
hist = cv2.calcHist(
[img], channels=[ch], mask=None, histSize=[n_bins], ranges=hist_range
)
hist = hist.squeeze(axis=-1)
hists.append(hist)
def plot_hist(bins, hist, color):
centers = (bins[:-1] + bins[1:]) / 2
widths = np.diff(bins)
ax.bar(centers, hist, width=widths, color=color)
bins = np.linspace(*hist_range, n_bins + 1)
fig, ax = plt.subplots()
ax.set_xticks([0, 256])
ax.set_xlim([0, 256])
ax.set_xlabel("Pixel Value")
for hist, color in zip(hists, channels.values()):
plot_hist(bins, hist, color=color)
plt.show()
运行效果:
图像 | 直方图 |
🦄 四、灰度直方图
(1)、灰度直方图定义
图像的灰度直方图:
描述了图像中灰度分布情况, 能够很直观的展示出图像中各个灰度级所占的多少。很好地体现出图像的亮度和对比度信息:灰度图分布居中说明亮度正常,偏左说明亮度较暗,偏右表明亮度较高;狭窄陡峭表明对比度降低,宽泛平缓表明对比度较高。
图像的灰度直方图是灰度级的函数, 描述的是图像中具有该灰度级的像素的个数: 其中,
横坐标是灰度级(横坐标代表像素值的取值区间),
纵坐标是该灰度级出现的率(纵坐标代表每一像素值在图像中的像素总数或者所占的百分比)。
根据直方图的形态可以判断图像的质量,通过调控直方图的形态可以改善图像的质量。
(2)、灰度直方图函数
OpenCV 提供了函数 cv2.calcHist 可以计算直方图,Numpy 中的函数 np.bincount 也可以实现同样的功能。
cv2.calcHist()函数的作用:
通过直方图可以很好的对整幅图像的灰度分布有一个整体的了解,直方图的x轴是灰度值(0~255),y轴是图片中具有同一个灰度值的点的数目。而calcHist()函数则可以帮助我们统计一幅图像的直方图。
cv2.calcHist(images,channels,mask,histSize,ranges)
该函数有5个参数:
- images: 输入图像,原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
- channels: 传入图像的通道,同样用中括号括来它会告函数我们统幅图像的直方图。如果入图像是灰度图,只有一个通道,它的值就是 [0];如果是彩色图像,有3个通道,传入的参数可以是 [0][1][2] 它们分别对应着 BGR 各个通道。这个值也得用[]传入。
- mask: 掩模图像。如果统计整幅图像的直方图,就把它为 None。但是如果你想统计图像某一分的直方图,就得制作一个掩模图像并使用它。
- histSize:BIN 的数目。灰度级的个数,也应用中括号括来,比如[250]
- ranges: 像素值的范围,像素值范围常为 [0 256],有的图像如果不是0-256,则需要调整后才可以。
绘制直方图需要的一些知识:灰度级,正常情况下就是0-255共256个灰度级,从最黑一直到最亮(白),那么每一个灰度级对应一个数来储存该灰度对应的点数目。也就是说直方图其实就是一个1*m(灰度级)的一个数组而已。但是有的时候我们不希望一个一个灰度的递增,比如现在我想15个灰度一起作为一个灰度级来花直方图,这个时候我们可能只需要1*(m/15)这样一个数组就够了。那么这里的15就是直方图的间隔宽度了。
如果您不需要单独查找所有像素值的像素数,而是在像素值间隔内查找像素数,该怎么办?例如,您需要找到介于 0 到 15 之间的像素数,然后是 16 到 31、...、240 到 255。您只需要 16 个值来表示直方图。因此,只需将整个直方图拆分为 16 个子部分,每个子部分的值就是其中所有像素计数的总和。这每个子部分都称为"BIN"。在第一种情况下,条柱数为256(每个像素一个),而在第二种情况下,它只有16。BINS 在 OpenCV 文档中由术语histSize表示。
1、 直方图反映了图像中的灰度分布规律。
它描述每个灰度级具有的像素个数, 但不包含这些像素在图像中的位置信息。
图像直方图不关心像素所处的空间位置, 因此不受图像旋转和平移变化的影响, 可以作为图像的特征。
2、 任何一幅特定的图像都有唯一的直方图与之对应, 但不同的图像可以有相同的直方图。
3、如果一幅图像有两个不相连的区域组成, 并且每个区域的直方图已知, 则整幅图像的直方图是该两个区域的直方图之和。
(3)、灰度直方图实例
①、绘制普通灰度直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('girl2a.jpg', 0) # 0表示灰度图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
plt.hist(img.ravel(),256);
cv2.waitKey(0)
plt.show()
运行效果:
灰度直方图 |
②、绘制多通道图像直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('cat1.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
cv2.imshow('img', img)
cv2.waitKey(0)
plt.show()
运行效果:
灰度直方图 |
🦄 五、使用掩模绘制直方图
cv2.calcHist()函数,其中参数mask用于标识是否使用掩模图像,当使用掩模图像获取直方图时,仅获取mask指定区域的直方图。
构造掩模图像时,通常先构造一个像素值都是0的二维数组,再将数组中指定区域的像素值设定为255,就得到了掩模图像。
例如:mask = np.zeros([600, 600], np.uint8)
mask[200:400, 200:400] = 255
该代码首先生成了一个600×600大小的黑色快,接着令行200-400、列200-400的区域为白色,由此得到掩模图像。
①、绘制方形掩模图像及直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread(r'girl2a.jpg', cv2.IMREAD_GRAYSCALE)
mask = np.zeros(img.shape, np.uint8)
mask[90:300, 50:280] = 250
histimg = cv2.calcHist([img], [0], None, [256], [0, 255])
histmaskimg = cv2.calcHist([img], [0], mask, [256], [0, 255])
image = cv2.add(img, np.zeros(np.shape(img), dtype=np.uint8), mask=mask)
cv2.imshow("img", img)
cv2.imshow("image", image)
plt.subplot(1, 2, 1)
plt.subplot(1, 2, 2)
plt.plot(histimg)
plt.plot(histmaskimg)
cv2.waitKey(0)
plt.show()
运行效果:
img | image |
②、绘制圆形掩模图像及直方图
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img1 = cv2.imread("girl2a.jpg")
Mask1 = np.zeros((img1.shape[0], img1.shape[1]), dtype=np.uint8)
Mask2 = Mask1.copy()
cv2.circle(Mask1, (150, 200), 110, (255, 255, 255), -1) # -1 实心
imgAddMask1 = cv2.add(img1, np.zeros(np.shape(img1), dtype=np.uint8), mask=Mask1) # 取圆
cv2.imshow("imgAddMask1", imgAddMask1)
hist = cv2.calcHist([imgAddMask1], [0], None, [256], [0, 256])
plt.hist(imgAddMask1.ravel(),256);
cv2.waitKey(0)
plt.show()
运行效果:
img | image |
🦄 六、总结
本文介绍了计算机视觉里直方图的原理和应用。
下篇文章我们学习一下如何通过直方图调整图片效果。
直方图均衡化是一种经典的图像处理算法,用以改善图像的亮度和对比度。
对图像进行直方图均衡化的目的是,使其原本分布集中的像素值,均衡的分布到所有可取值的范围,这样,图像就既有明亮也有灰暗,对比度和亮度就得到了改善。
通过直方图均衡化调整后的图像对比效果图如下:
推荐阅读:
计算机视觉__基本图像操作(显示、读取、保存) | 直方图(颜色直方图、灰度直方图) | 直方图均衡化(调节图像亮度、对比度) |