Одинаковые математические и числовые функции не дают одинакового результата при применении к значениям пикселей - PullRequest
3 голосов
/ 23 мая 2019

Я хочу рассчитать Воспринимаемая яркость на основе формулы из этой ссылки этого изображения:

enter image description here

Идея состоит в том, чтобы зациклить каждый пиксель и вычислить его Воспринимаемую яркость по следующей формуле:

Pb = sqrt (0,241 R² + 0,691 G² + 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

Есть ли какое-либо объяснение, почему я получил разные результаты с изображением и схожие результаты со вторым массивом?(это может быть связано с типом элементов массива?)
Примечание: Я использую

python==3.6
numpy==1.16.1  
opencv-contrib-python==4.0.0.21  
opencv-python==4.0.0.21  

Ответы [ 2 ]

4 голосов
/ 23 мая 2019

Проблема связана с различием правил преобразования типов данных между 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), что позволяет избежать проблемы усечения.

3 голосов
/ 23 мая 2019

Проблема заключается в функции 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)

Как видите, функция power не изменила тип ... и это приводитпереполниться ...

У вас есть 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 и правильные числа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...