介绍
不熟悉摄影,“Try-X 是完美的”“背光获胜”“世界的三分之一”“不允许山核桃”“顶空是敌人”我只有有偏见的知识。
这就是为什么几年前我买了一台稍贵的数码相机时,我不知道液晶显示屏上显示的图形是什么的原因。
但是现在我已经在 OpenCV 中使用图像处理获得了很多乐趣,我知道。这是一个亮度直方图。
在 Python 中创建直方图
直方图不是专门用于图像处理的技术。它被广泛用作统计方法之一,并以七大质量控制工具之一而闻名。
在 Python 中,OpenCV 和 numpy 具有获取直方图的功能。
在统计界,有关于条形图的个数和宽度的讨论,但如果是图像亮度的直方图,则是“256个类别,从0到255(包括255),宽度为1”。将是正常的
这次的图像是 Rena 小姐,她的 RGB 是适度偏向的。
| 莲娜.png |
|---|
开放式CV
在 OpenCV 中cv2.calcHist()利用。只写所需的参数
hist = cv2.calcHist(images, channels, mask, histSize, ranges)
变成。从参数名称images、channels 可以看出,它们指定了列表。元组也是可能的。
争论
- 图像 输入图像列表。指定为
[image]和元素 1 的列表。除了np.uint8,np.float32也可以。 - channels 颜色通道。为灰度图像指定
[0],为 BGR 指定[0][1][2]。 - 蒙版蒙版图像。如果未指定,请使用
None。我希望这是可选的。 - histSize 间隔数。亦称仓。如果要单独计算 256 个渐变,请使用 [256]。
- ranges 要计数的值范围。对图像的所有亮度执行时设置为 [0, 256]。不是 [0, 255]。
输出
- hist 直方图。当图像数量为 1 时,返回一个 2D numpy 数组,例如
[[0.000e+00], [0.000e+00], …]。每个元素都是一个 float32。
就这个返回值而言,即使images是一个列表,似乎也不可能一次指定多张图片并获取多个直方图。
样本
为 RGB 图像的每个颜色通道创建直方图的示例。
请注意,颜色序列是 BGR,因为它是 OpenCV。
import cv2
import matplotlib.pyplot as plt
filename = "lenna.png"
image = cv2.imread(filename)
COLORS = ["blue","green","red"]
for i, color in enumerate(COLORS):
hist = cv2.calcHist([image], [i], None, [256], [0, 256])
plt.plot(hist, color=color)
plt.show()
结果,确认莉娜图像偏红。
数字货币
在 numpy 中np.histogram()利用。
hist, bin_edges = np.histogram(a, bins=10, range=None, normed=None, weights=None, density=None)
如何使用。
论据(仅部分)
- 一个输入矩阵。预先进行一维化的情况很多,但实际上在计算过程中一维化。
- bins 箱数。可选,默认为 10。它可以是数字、列表或元组(任何东西都可以是字符串)。
- 范围 Bin 范围。可以省略,初始值为a的最小值到最大值。
输出
- 直方图。一维 numpy 数组,其中每个元素都是整数。
- bin_edges Bin 范围。
样本 1 省略范围
尝试使用指定的bins 和未指定的range 进行绘图。
for i, color in enumerate(COLORS):
hist, bin_edges = np.histogram(image[:, :, i], bins=256)
plt.plot(hist, color=color)
仅指定 np.histogram() bins
|
cv2.calcHist() |
|---|---|
注意红色。 Lena图像整体呈现强烈的红色,应该没有亮度小于等于50的点,但是在这个图中,正常图中50到255的范围已经扩大到0到255。这是“如果省略范围,则从最小值到最大值”的结果。
图被切碎的原因是当将“从最小值到最大值”扩展到“从0到255的256个类”时,会出现“从100.1到100.9”这样的不完整范围。这被认为是因为由于亮度是整数值,因此“从 100.1 到 100.9”范围内的点数为 0。
示例 2 指定范围
接下来,尝试指定bins 和range。
for i, color in enumerate(COLORS):
hist, bin_edges = np.histogram(image[:, :, i], bins=256, range=(0, 256))
plt.plot(hist, color=color)
指定 np.histogram() bins 和 range
|
cv2.calcHist() |
|---|---|
原以为还有很长的路要走,但我能画出几乎一样的图表。
这真的正确吗?
检查正确性
Lena 图像大小为 512x512,像素为 262144。如果您采用直方图,则频率总和应为 262144,无论箱数如何。
此外,在此图像中,红色强度 = 255 的像素是
np.sum(image[:, :, 2]==255) # 112
事实证明,其中有 112 个。
示例 1 范围 =(0, 254)
让我们指定range=(0, 254) 作为一个明显不正确的例子。垃圾箱的数量无关紧要,因此请将其保留为默认值。
hist, bin_edges = np.histogram(image[:, :, 2], range=(0, 254))
len(hist) # 10 ビン数(デフォ値)
sum(hist) # 262032 度数の合計
这里出现的 262032 是总像素数 262144 减去亮度为 255、112 的像素数。这意味着不计算亮度为 255 的像素。
示例 2 bins=256, range=(0, 255)
接下来,让我们指定bins=256, range=(0, 255)。
hist, bin_edges = np.histogram(image[:, :, 2], bins=256, range=(0, 255))
len(hist) # 256 ビン数
sum(hist) # 262144 度数の合計
len(bin_edges) # 257 bin_edgesの数
bin_edges
[ 0. 0.99609375 1.9921875 2.98828125 3.984375
4.98046875 5.9765625 6.97265625 7.96875 8.96484375
中略
249.0234375 250.01953125 251.015625 252.01171875 253.0078125
254.00390625 255. ]
可以看出,频率之和等于像素数,所有像素都被计算在内。
顺便说一句, bin_edges 有 (length(hist)+1) 元素。
0 开头,255 结尾。最后一个是第 257 个,而不是第 256 个。虽然它被 256 个 bin 分隔,但亮度 255 属于它之外的第 257 个簇。情况不妙。
另外,我不喜欢每个类都是一个不可分割的数字,而不是一个精确的间隔。
示例 3 bins=256, range=(0, 256)
接下来,让我们指定bins=256, range=(0, 256)。
hist, bin_edges = np.histogram(image[:, :, 2], bins=256, range=(0, 256))
len(hist) # 256 ビン数
sum(hist) # 262144 度数の合計
len(bin_edges) # 257 bin_edgesの数
bin_edges
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.
14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27.
中略
238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251.
252. 253. 254. 255. 256.]
现在值 255 最终属于第 256 个集群。我们还确认每个类的宽度为 1。
这周围这个网站最好关注一下
比较 OpenCV 直方图和 Numpy 直方图
cv2.calcHist() 没有在 np.histogram() 中的bin_edges 的输出。让我们看看这两个直方图是否完美匹配。
输出的 dtype 和 shape 再次表示如下。
| cv2.calcHist() | np.histogram() | → | 统一格式 | |
|---|---|---|---|---|
| 类型 | dtype('float32') | dtype('int64') | → | 使它成为 dtype('float32') |
| 形状 | (256, 1) | (256,) | → | 设置为 (256,) |
让我们统一这个区域,检查内容是否完全匹配。还要检查处理时间。
似乎有一种更复杂的方式来编写处理时间测量部分,但它离题了,所以我在这里写得很扎实。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import time
COLORS = ["blue","green","red"]
filename = "lenna.png"
image = cv2.imread(filename)
for i, color in enumerate(COLORS):
print("-" * 20)
print(color)
# cv2.calcHist() リストでなくタプルで指示してみる 良い子は真似するな
start_time = time.time()
hist_cv = cv2.calcHist((image,), (i,), None, histSize=(256,), ranges=(0, 256))
end_time = time.time()
print("cv2.calcHist()", end_time - start_time)
# np.histogram() rangeをリストで指示してみる
start_time = time.time()
hist_np, bin_edges = np.histogram(image[:, :, i], bins=256, range=[0, 256])
end_time = time.time()
print("np.histogram()", end_time - start_time)
hist_cv_sameformat = hist_cv.flatten() # (256, 1) -> (256,)
hist_np_sameformat = hist_np.astype(np.float32) # int64 -> float32
if np.array_equal(hist_cv_sameformat, hist_np_sameformat): # 本当はarray_equalはdtypeを揃える必要はない
print("完全一致")
else:
print("不一致あり")
这是结果。
--------------------
blue
cv2.calcHist() 0.0020308494567871094
np.histogram() 0.005982637405395508
完全一致
--------------------
green
cv2.calcHist() 0.000997781753540039
np.histogram() 0.003998756408691406
完全一致
--------------------
red
cv2.calcHist() 0.0010008811950683594
np.histogram() 0.003998994827270508
完全一致
我能够使用 OpenCV 和 numpy 获得相同的结果。我还发现 OpenCV 要快得多。
发现参数,无论其名称如何,都以几乎相同的方式使用。如果一个是 (0, 255) 而另一个是 [0, 256] 或类似的东西,你甚至不能看它。
在最后
我打算省略 np.histogram() 的bin_edges 的解释,但通过提及这一点,我现在可以理解范围不合适时的行为。这是你无法学习的东西,除非你自己做。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308627088.html