numpy.ndarray с формой (высота, ширина, n) из n значений на пиксель изображения - PullRequest
0 голосов
/ 28 октября 2018

Мой ввод PIL.Image.Image в режиме RGB или RGBA, и мне нужно заполнить numpy.ndarray тремя значениями с плавающей запятой, рассчитанными из значений RGB каждого пикселя.Выходной массив должен быть индексируемым по координатам пикселей.Я нашел следующий способ сделать это:

import numpy as np
from PIL import Image

def generate_ycbcr(img: Image.Image):
    for r, g, b in img.getdata():
        yield 0.299 * r + 0.587 * g + 0.114 * b
        yield 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
        yield 128 + 0.5 * r - 0.418688 * g - 0.081312 * b

def get_ycbcr_arr(img: Image.Image):
    width, height = img.size
    arr = np.fromiter(generate_ycbcr(img), float, height * width * 3)
    return arr.reshape(height, width, 3)

Это работает, но я подозреваю, что есть лучший и / или более быстрый способ.Скажите, пожалуйста, есть ли он, но также если его нет.

Примечание: я знаю, что могу convert() изображение до YCbCr, а затем заполнить numpy.array от этого, но преобразованиеокругляется до целых значений, а это не то, что мне нужно.

1 Ответ

0 голосов
/ 28 октября 2018

Для начала, вы можете преобразовать изображение непосредственно в массив numpy и использовать векторизованные операции, чтобы сделать то, что вы хотите:

def get_ycbcr_vectorized(img: Image.Image):
    R,G,B = np.array(img).transpose(2,0,1)[:3] # ignore alpha if present
    Y = 0.299 * R + 0.587 * G + 0.114 * B
    Cb = 128 - 0.168736 * R - 0.331264 * G + 0.5 * B
    Cr = 128 + 0.5 * R - 0.418688 * G - 0.081312 * B
    return np.array([Y,Cb,Cr]).transpose(1,2,0)

print(np.array_equal(get_ycbcr_arr(img), get_ycbcr_vectorized(img))) # True

Однако вы уверены, что непосредственное преобразование в 'YCbCr' будетсильно отличается?Я проверил преобразование, определенное в приведенной выше функции:

import matplotlib.pyplot as plt
def aux():
    # generate every integer R/G/B combination
    R,G,B = np.ogrid[:256,:256,:256]
    Y = 0.299 * R + 0.587 * G + 0.114 * B
    Cb = 128 - 0.168736 * R - 0.331264 * G + 0.5 * B
    Cr = 128 + 0.5 * R - 0.418688 * G - 0.081312 * B

    # plot the maximum error along one of the RGB channels
    for arr,label in zip([Y,Cb,Cr], ['Y', 'Cb', 'Cr']):
        plt.figure()
        plt.imshow((arr - arr.round()).max(-1))
        plt.xlabel('R')
        plt.ylabel('G')
        plt.title(f'max_B ({label} - {label}.round())')
        plt.colorbar()

aux()   
plt.show()

Результаты показывают, что самая большая абсолютная ошибка равна 0,5, хотя эти ошибки происходят во всех пикселях:

image Y error"> image Cb error"> image Cr error">

Так что, да, это может быть большой относительный относительный ошибка, но это не обязательно огромная проблема.

В случае, если достаточно встроенного преобразования:

arr = np.array(img.convert('YCbCr'))

- это все, что вам нужно.

...