Numpy: самый быстрый способ правильно обрезать цвета RGB

avatar
Dr. Volker Jaenisch
9 августа 2021 в 00:36
141
1
0

Данный трехмерный массив плавающих пикселей RGB с размерами X, Y, RGB.

rgb = array([[[ 1.11204494,  0.17241406,  0.14145795],
              [ 0.98240261,  0.16112416,  0.11333147]],

             [[ 0.1320549 ,  0.11862232,  0.16630839],
              [ 0.10424024,  1.12368929,  0.15233576]]])

Пиксели 0 и 3 превышают предел 1,0. Правильное отсечение — установить для всего пикселя значение [1.0,1.0,1,0].

rgb = array([[[ 1.0       ,  1.0,         1.0],
              [ 0.98240261,  0.16112416,  0.11333147]],

             [[ 0.1320549,  0.11862232,  0.16630839],
              [ 1.0,        1.0       ,  1.0]]])

Мой способ сделать это очень прост:

x,y,c = np.where(rgb > 1.0)
rgb[x, y, :] = 1.0

Есть ли более быстрый способ?

Любая помощь приветствуется

Ура Волкер

Источник
Mark Setchell
9 августа 2021 в 01:43
1

Не рассчитано время, но я думаю, что вы можете начать с np.any(rgb,axis=2), поэтому вам не нужно тестировать все 3 элемента, если любой из первых 2 элементов превышает 1,0

Alex Alex
9 августа 2021 в 01:50
1

У Марка Сетчелла есть хорошая идея: rgb[np.any(rgb>1,axis=2)]=1.0

Mark Setchell
9 августа 2021 в 01:55
0

@AlexAlex Спасибо, что исправили мое упущение 👍

Alex Alex
9 августа 2021 в 02:16
0

@Марк Сетчелл, можешь написать ответ?

Mark Setchell
9 августа 2021 в 02:26
0

@AlexAlex Здесь, в Великобритании, уже поздно, поэтому, пожалуйста, напишите, если хотите. В противном случае я сделаю это завтра.

Mark Setchell
9 августа 2021 в 10:38
0

Не могли бы вы указать размер вашего изображения - я имею в виду X и Y - чтобы мои тайминги были реалистичными? Спасибо.

Ответы (1)

avatar
Mark Setchell
9 августа 2021 в 11:14
0

Похоже, что идея np.any() не сильно помогла. Я также пытался использовать OpenCV, используя cv2.inRange(), но безрезультатно.

Я предположил 1080p для размера вашего изображения (но вы можете указать любой размер), и я предположил, что около 10% ваших пикселей могут нуждаться в отсечении (путем установки диапазона случайных значений), и попробовал некоторые варианты следующим образом:

#!/usr/bin/env python3

import numba
import numpy as np
from numba import jit, prange

def method1(rgb):
   # Original method
   x,y,c = np.where(rgb > 1.0)
   rgb[x, y, :] = 1.0
   return rgb

def method2(rgb):
   # np.any(axis=2) method
   rgb[np.any(rgb>1,axis=2)]=1.0
   return rgb

@jit(nopython=True,parallel=True)
def method3(rgb):
   # Numba
   h, w = rgb.shape[:2]
   for y in prange(h):
      for x in range(w):
         p = rgb[y,x]
         if p[0]>1.0 or p[1]>1.0 or p[2]>1.0:
            rgb[y,x] = [1.0,1.0,1.0]
   return rgb


# Create standard image
X, Y = 1920, 1080
np.random.seed(42)
rgb = np.random.uniform(high=1.1,size=(Y,X,3))

res = method1(rgb)
#res = method2(rgb)
#res = method3(rgb)

Мои тайминги показывают, что Numba является явным победителем с коэффициентом 8-9:

In [58]: %timeit res = method1(rgb)
16.6 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [59]: %timeit res = method2(rgb)
24.8 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [60]: %timeit res = method3(rgb)
1.93 ms ± 21.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Dr. Volker Jaenisch
9 августа 2021 в 15:33
0

Спасибо всем и особенно @Mark Setchell за быстрый ответ! Я попробую numba, если это применимо. Поскольку вы распараллелили, сколько процессорных ядер вы использовали? Код будет работать на raspberryPI, а ядер всего четыре. Также я не знаю производительности акций-памяти. А что насчет потребления оперативной памяти.

Mark Setchell
9 августа 2021 в 17:20
0

Это должно нормально работать на Raspberry Pi — у вас есть код, так что просто попробуйте его со своими изображениями. Потребление оперативной памяти должно быть низким, потому что это делается на месте — вы можете измерить его с помощью /usr/bin/time -l script.py

Mark Setchell
9 августа 2021 в 17:22
0

Вы также можете попробовать преобразовать в тип данных float32, чтобы сравнить скорость, т.е. method3(rgb.astype(np.float32))