OpenCV:图像直方图计算

OpenCV:图像直方图计算

图像直方图为图像中像素强度的分布提供了有价值的见解。通过了解直方图,你可以获得有关图像对比度、亮度和整体色调分布的信息。这些知识对于图像增强、图像分割和特征提取等任务非常有用。

本文旨在为学习如何使用 OpenCV 执行图像直方图计算提供清晰且全面的指南。通过理解和应用直方图分析技术,你可以提高图像质量、执行阈值操作、分析颜色成分、提取有用的特征以及更有效地可视化和理解图像。

图像直方图

每个图像都由单独的像素组成,就像网格上的小点一样。假设我们有一个大小为 250 列和 100 行的图像,总共 2500 个像素。每个像素都可以有不同的颜色值,用 0 到 255 范围内的数字表示。

为了可视化图像中颜色值的分布,我们可以创建直方图。该直方图充当一组条形图,显示具有相同颜色值的像素数。通过比较条形的高度,我们可以轻松识别图像中哪些颜色值更突出或更频繁地出现。这种图形表示为图像的整体颜色组成和分布提供了宝贵的见解。

图像直方图(单色)

现在记住,像素强度

0 → 黑色

255 → 白色

因此,如果我们的直方图向左移动(左偏),则图像会包含更多黑色像素;如果我们的直方图向右移动(右偏),图像会包含更多白色像素。

左偏和右偏直方图

所以我相信你已经完全理解了,

  • 更黑→更暗的图像

  • 更白→更亮的图像

现在让我们在 OpenCV 中进行直方图计算。

首先,我们将加载图像并将其可视化。

复制代码
#import necessary libraries
import cv2
import numpy as np
import matplotlib.pyplot as plt#using opencv to read an image
#BGR Image
image = cv2.imread("C:/users/public/pictures/nature.jpg")#visualizing
cv2.namedWindow("BGR Image", cv2.WINDOW_NORMAL);
cv2.imshow("BGR Image",image);cv2.waitKey(0) & 0xFF 
cv2.destroyAllWindows()

在绘制直方图之前,我们可以分离该图像中的颜色通道。

复制代码
B = image[:,:,0] #blue layer
G = image[:,:,1] #green layer
R = image[:,:,2] #red layer

现在我们使用 OpenCV 函数 cv.calcHist() 计算并找到每一层的直方图,并使用 OpenCV 和 Matplotlib 函数绘制这些直方图

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

● images:uint8 或float32 类型的源图像。它应该放在方括号中,即"[img]"。

● channels:也在方括号中给出。它是我们计算直方图的通道的索引。例如,如果输入是灰度图像,则其值为[0]。对于彩色图像,可以通过[0]、[1]或[2]分别计算蓝色、绿色或红色通道的直方图。

● mask:蒙版图像。为了找到整个图像的直方图,它被指定为"None"。但是,如果你想找到图像特定区域的直方图,则必须为其创建一个蒙版图像并将其作为蒙版。

● histSize:BIN 计数。需要在方括号中给出。对于全尺寸,我们通过[256]。

● ranges:范围。通常是[0,256]。

复制代码
B_histo = cv2.calcHist([image],[0], None, [256], [0,256])
G_histo = cv2.calcHist([image],[1], None, [256], [0,256])
R_histo = cv2.calcHist([image],[2], None, [256], [0,256])

现在我们使用 matplotlib 将它们绘制在子图中。

你可以在不同设置的图像上尝试此操作。

完整代码

复制代码
#import necessary libraries
import cv2
import numpy as np
import matplotlib.pyplot as plt#using opencv to read an image
#BGR Image
image = cv2.imread("C:/users/public/pictures/nature.jpg")#seperating colour channels
B = image[:,:,0] #blue layer
G = image[:,:,1] #green layer
R = image[:,:,2] #red layer#calculating histograms for each channel
B_histo = cv2.calcHist([image],[0], None, [256], [0,256])
G_histo = cv2.calcHist([image],[1], None, [256], [0,256])
R_histo = cv2.calcHist([image],[2], None, [256], [0,256])#visualizing histograms
plt.subplot(2, 2, 1)
plt.plot(B_histo, 'b')
plt.subplot(2, 2, 2)
plt.plot(G_histo, 'g')
plt.subplot(2, 2, 3)
plt.plot(R_histo, 'r')#visualizing image
cv2.namedWindow("BGR Image", cv2.WINDOW_NORMAL);
cv2.imshow("BGR Image",image);
cv2.waitKey(0) & 0xFF 
cv2.destroyAllWindows()

曝光过度和曝光不足的图像

然后我们可以扩展这个想法来识别曝光过度(太亮)的图像和曝光不足(太暗)的图像。

让我们看看这些图像的直方图。

使用 Matplotib 和 OpenCV 绘制直方图

显然,一个直方图左偏,表示图像曝光不足,而另一直方图右偏,表示图像曝光过度。

在这里,我们只需查看直方图就可以清楚地了解图像是否曝光不足或曝光过度。

直方图均衡

考虑曝光不足或曝光过度的图像,其像素值仅局限于某个特定的值范围。

例如:较亮的图像将所有像素限制为高值。

**但是一个好的图像将具有来自图像的所有区域的像素。**所以你需要将这个直方图拉伸到两端。这通常会提高图像的对比度。

当对彩色图像执行直方图均衡时,我们通常将该过程分别应用于图像中RGB颜色值的红色、绿色和蓝色分量。

首先,我们读取图像并将图像分成三个颜色层。

复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt#using opencv to read an image
#BGR Image
image = cv2.imread("C:/users/public/pictures/underexposed_image.jpg")#seperating colour channels
B = image[:,:,0] #blue layer
G = image[:,:,1] #green layer
R = image[:,:,2] #red layer

然后我们使用 cv.equalizeHist () 来均衡每个颜色层的直方图。使用 Matplotlib 和 OpenCV 将它们可视化。

复制代码
b_equi = cv2.equalizeHist(B)
g_equi = cv2.equalizeHist(G)
r_equi = cv2.equalizeHist(R)plt.imshow(b_equi)
plt.title("b_equi")
plt.show()
plt.imshow(g_equi)
plt.title("g_equi")
plt.show()
plt.imshow(r_equi)
plt.title("r_equi")
plt.show()

使用 OpenCV 均衡 R、G 和 B 层

通过均衡的颜色层,我们使用 cv.calcHist() 计算每种颜色的直方图。然后将它们全部绘制出来。

复制代码
B_histo = cv2.calcHist([b_equi],[0], None, [256], [0,256]) 
G_histo = cv2.calcHist([g_equi],[0], None, [256], [0,256])
R_histo = cv2.calcHist([r_equi],[0], None, [256], [0,256])plt.subplot(2, 2, 1)
plt.plot(G_histo, 'g')
plt.subplot(2, 2, 2)
plt.plot(R_histo, 'r')
plt.subplot(2, 2, 3)
plt.plot(B_histo, 'b')

你一定已经注意到,我们在"channels"位置仅使用了 [0]。在前面的例子中,我们使用了所有[0]、[1]和[2]。这是由于分离通道的可用性。因此只有 1 个通道。因此,对于所有直方图,"channels"为 [0]

或者,你可以获取原始图像中每个通道的直方图,并使用均衡后的颜色层绘制它们。

复制代码
#calculate histograms for each channel seperately
#Equilized channels
B_histo = cv2.calcHist([b_equi],[0], None, [256], [0,256]) 
G_histo = cv2.calcHist([g_equi],[0], None, [256], [0,256])
R_histo = cv2.calcHist([r_equi],[0], None, [256], [0,256])
#Original channels
BO_histo = cv2.calcHist([image],[0], None, [256], [0,256]) 
GO_histo = cv2.calcHist([image],[1], None, [256], [0,256])
RO_histo = cv2.calcHist([image],[2], None, [256], [0,256])#visualize the channel histograms seperately
plt.figure(figsize=(10,12), )plt.subplot(3, 2, 1)
plt.title("Green Original")
plt.plot(GO_histo, 'g')plt.subplot(3, 2, 2)
plt.title("Green Equilized")
plt.plot(G_histo, 'g')plt.subplot(3, 2, 3)
plt.title("Red Original")
plt.plot(RO_histo, 'r')plt.subplot(3, 2, 4)
plt.title("Red Equilized")
plt.plot(R_histo, 'r')plt.subplot(3, 2, 5)
plt.title("Blue Original")
plt.plot(BO_histo, 'b')plt.subplot(3, 2, 6)
plt.title("Blue Equilized")
plt.plot(B_histo, 'b')

s

原始图像颜色直方图与均衡图像颜色直方图

继续下一步,我们现在拥有的只是层。为了从中获得图像,我们需要合并它们。

复制代码
equi_im = cv2.merge([b_equi,g_equi,r_equi])

现在让我们并排查看均衡后的图像和原始图像。

复制代码
cv2.namedWindow("Original Image", cv2.WINDOW_NORMAL);
cv2.imshow("Original Image",image);
cv2.namedWindow("New Image", cv2.WINDOW_NORMAL);
cv2.imshow("New Image",equi_im);cv2.waitKey(0) & 0xFF 
cv2.destroyAllWindows()

使用 OpenCV 均衡图像

完整代码

复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt#using opencv to read an image
#BGR Image
image = cv2.imread("C:/users/public/pictures/underexposed_image.jpg")#seperating colour channels
B = image[:,:,0] #blue layer
G = image[:,:,1] #green layer
R = image[:,:,2] #red layer#equilize each channel seperately
b_equi = cv2.equalizeHist(B)
g_equi = cv2.equalizeHist(G)
r_equi = cv2.equalizeHist(R)#calculate histograms for each channel seperately
B_histo = cv2.calcHist([b_equi],[0], None, [256], [0,256]) 
G_histo = cv2.calcHist([g_equi],[0], None, [256], [0,256])
R_histo = cv2.calcHist([r_equi],[0], None, [256], [0,256])#merge thechannels and create new image
equi_im = cv2.merge([b_equi,g_equi,r_equi])#visualize the equilized channels seperately
plt.imshow(b_equi)
plt.title("b_equi")
plt.show()
plt.imshow(g_equi)
plt.title("g_equi")
plt.show()
plt.imshow(r_equi)
plt.title("r_equi")
plt.show()#visualize the channel histograms seperately
plt.subplot(2, 2, 1)
plt.plot(G_histo, 'g')
plt.subplot(2, 2, 2)
plt.plot(R_histo, 'r')
plt.subplot(2, 2, 3)
plt.plot(B_histo, 'b')#visualize the original and equilized images
cv2.namedWindow("Original Image", cv2.WINDOW_NORMAL);
cv2.imshow("Original Image",image);
cv2.namedWindow("New Image", cv2.WINDOW_NORMAL);
cv2.imshow("New Image",equi_im);cv2.waitKey(0) & 0xFF 
cv2.destroyAllWindows()
相关推荐
guoji77884 分钟前
安全与对齐的深层博弈:Gemini 3.1 Pro 安全护栏与对抗测试深度拆解
人工智能·安全
实在智能RPA12 分钟前
实在 Agent 和通用大模型有什么不一样?深度拆解 AI Agent 的感知、决策与执行逻辑
人工智能·ai
独隅17 分钟前
PyTorch 模型部署的 Docker 配置与性能调优深入指南
人工智能·pytorch·docker
lihuayong24 分钟前
OpenClaw 系统提示词
人工智能·prompt·提示词·openclaw
黑客说38 分钟前
AI驱动剧情,解锁无限可能——AI游戏发展解析
人工智能·游戏
踩着两条虫43 分钟前
AI驱动的Vue3应用开发平台深入探究(十):物料系统之内置组件库
android·前端·vue.js·人工智能·低代码·系统架构·rxjava
小仙女的小稀罕1 小时前
听不清重要会议录音急疯?这款常见AI工具听脑AI精准转译
开发语言·人工智能·python
reesn1 小时前
qwen3.5 0.8B纠正任务实践
人工智能·语言模型
实在智能RPA1 小时前
实在Agent 制造业落地案例:探寻工业大模型从实验室走向车间的实战路径
人工智能·ai