我要根据这张图片的link公式计算感知的亮度:
想法是遍历每个像素,并根据以下公式计算其感知的亮度:
Pb = sqrt(0.241R²+ 0.691G²+ 0.068B²)
然后将所有值求和并计算平均值。
这是我写的代码:
import cv2
from math import sqrt
img = cv2.imread('e.png')
H, W = img.shape[:2]
pr = 0.241
pg = 0.691
pb = 0.068
p = []
for h in range(0, H):
for w in range(0, W):
p.append(sqrt(pr * pow(img[h][w][2], 2) + pg * pow(img[h][w][1], 2) + pb * pow(img[h][w][0], 2)))
arr = np.reshape(p, (H, W))
cv2.imwrite('loop_img.jpg', arr)
print(np.mean(arr))
我最后得到的图像是:
但是,当我使用numpy重复相同的过程(以避免循环遍历每个像素)时,我得到了不同的值!
这是我使用的代码:
import cv2
import numpy as np
img = cv2.imread('e.png')
b, g, r = cv2.split(img)
pr = 0.241
pg = 0.691
pb = 0.068
P = np.sqrt(pr * pow(r, 2) + pg * pow(g, 2) + pb * pow(b, 2))
cv2.imwrite('np_img.jpg', P)
print(np.mean(P))
我得到的图像是这样:
最奇怪的是,当我在随机的numpy数组上应用相同的方法时,我得到了相似的结果!
import numpy as np
import cv2
from math import sqrt
pr = 0.241
pg = 0.691
pb = 0.068
arr = np.array([[[255, 127, 0],
[255, 127, 0]],
[[255, 133, 0],
[255, 133, 0]],
[[255, 138, 0],
[255, 138, 0]]])
b, g, r = cv2.split(arr)
p = []
for h in range(0, 3):
for w in range(0, 2):
print(arr[h][w])
p.append(sqrt(pr * pow(arr[h][w][2], 2) + pg * pow(arr[h][w][1], 2) + pb * pow(arr[h][w][0], 2)))
arr_p = np.reshape(p, (3, 2))
print('arr_p:', arr_p)
np_p = np.sqrt(pr * pow(r, 2) + pg * pow(g, 2) + pb * pow(b, 2))
print('np_ap:', np_p)
print('loop_mean:', np.mean(arr_p))
print('numpy_mean:', np.mean(np_p))
我得到的结果:
arr_p: [[124.7671391 124.7671391 ]
[129.01472397 129.01472397]
[132.59375551 132.59375551]]
np_ap: [[124.7671391 124.7671391 ]
[129.01472397 129.01472397]
[132.59375551 132.59375551]]
loop_mean: 128.79187285939827
numpy_mean: 128.79187285939827
请问为什么我的图像得到不同的结果而第二个数组得到相似的结果? (可能与数组元素类型有关吗?)
NB:我使用
python==3.6
numpy==1.16.1
opencv-contrib-python==4.0.0.21
opencv-python==4.0.0.21
答案 0 :(得分:4)
问题是由于numpy array
和原始数据类型之间的数据类型转换规则不同。
对于numpy数组,计算过程如下:
P = np.sqrt(pr * pow(r, 2) + pg * pow(g, 2) + pb * pow(b, 2))
此处的罪魁祸首是pow
。由于使用cv2.imread
读取的图像的默认数据类型为np.uint8
,因此r
,g
和b
也具有相同的类型。现在,将pow
函数应用于numpy数组时,结果数组往往具有相同的整数类型。结果中的值将被截断为uint8
类型的范围,从而导致无效的结果。由于结果被截断,因此所观察到的平均值变得很小。
1。将输入图像转换为浮点类型:
img = cv2.imread('e.png')
img = img.astype(np.float)
2。在pow
中使用浮点操作数:
P = np.sqrt(pr * pow(r, 2.0) + pg * pow(g, 2.0) + pb * pow(b, 2.0))
p.append(sqrt(pr * pow(img[h][w][2], 2) + pg * pow(img[h][w][1], 2) + pb * pow(img[h][w][0], 2)))
显然,将pow
应用于单个整数而不是numpy array
会产生较大整数类型(int64
)的值,从而避免了截断的问题。
答案 1 :(得分:3)
问题是pow
数组的np.uint8
函数。首先,让我们举一个简单的例子:
>> a = np.arange(20, dtype=np.uint8).reshape(4,5)
给出:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]], dtype=uint8)
使用np.uint8(已加载图像的类型)进行测试很重要。然后,我们执行pow或np.power(它们的行为完全相同),结果如下:
>> np.power(a,2)
array([[ 0, 1, 4, 9, 16],
[ 25, 36, 49, 64, 81],
[100, 121, 144, 169, 196],
[225, 0, 33, 68, 105]], dtype=uint8)
>> pow(a,2)
array([[ 0, 1, 4, 9, 16],
[ 25, 36, 49, 64, 81],
[100, 121, 144, 169, 196],
[225, 0, 33, 68, 105]], dtype=uint8)
如您所见,幂函数没有更改类型...这导致溢出...
您有2种解决方案:
先投射类型,然后再像变回一样
b = np.float32(b) #same for g and r or to the whole image
# or this
b, g, r = cv2.split(np.float32(img))
然后在保存使用np.uint8()
之前,opencv保存功能通常仅适用于uint8,也许新版本不起作用。
另一件事是使用np.float_power
,它将返回float32类型和正确的数字。