应用于像素值时,相同的math和numpy函数不会给出相同的结果

时间:2019-05-23 10:19:07

标签: python numpy opencv

我要根据这张图片的link公式计算感知的亮度

enter image description here

想法是遍历每个像素,并根据以下公式计算其感知的亮度

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))

我最后得到的图像是:

enter image description here
平均值是 82.04557421656007

但是,当我使用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))

我得到的图像是这样:

enter image description here
平均值是 1.6438602314083277

最奇怪的是,当我在随机的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  

2 个答案:

答案 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,因此rgb也具有相同的类型。现在,将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类型和正确的数字。