Сдвиг значений пикселей HSV в Python с помощью Numpy

Я пытаюсь преобразовать (сдвинуть) значения каждого пикселя в изображении HSV (взятом из кадра видео).

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

(Оттенок + 90)% 180 (в OpenCV 3 оттенок находится в диапазоне [0,180])

Вот что я придумал:

hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV);
H = hsv[:,:,0]
mask= [H<75 and H>128]
print("orig",hsv[mask])
hsv[mask] = ((hsv[mask]+90) % 180)

К сожалению, это не работает, поскольку при таком подходе я выбираю весь канал оттенка, а не его значения в пикселях.


person GodIsAnAstronaut    schedule 06.04.2018    source источник
comment
Вы используете запутанные имена переменных. Сначала вы используете H, S, V как 0, 1, 2, чтобы быть первым, вторым и третьим каналами, но затем вы переназначаете их как фактический 2D-массив вместо индекса для каждого канала. Затем вы используете S и V для индексации во 2-е и 3-е измерения, что, по-видимому, не то, что вы хотите сделать (вместо этого вы хотите [H, :, :], [S, :, :] и [V, :, :]), но вы можете просто сделать h, s, v = cv2.split(hsv), и ваша жизнь станет намного проще.   -  person alkasm    schedule 06.04.2018
comment
Я уже редактировал вопрос, так как понял, что код очень неправильный   -  person GodIsAnAstronaut    schedule 06.04.2018
comment
К сожалению, я имел в виду [:, :, H], но, похоже, вы это уловили. Ваш новый код должен работать, но, поскольку это множество массивов, вы захотите сделать это: mask = (H<75) & (H>128). Тогда ваш код выше будет работать.   -  person alkasm    schedule 06.04.2018
comment
Ключевое слово and в Python работает только с логическими значениями, но H<75 является массивом логических значений, а не прямым логическим значением. Итак, для numpy массивов & делает то, что вы ожидаете (поэлементные логические операции).   -  person alkasm    schedule 06.04.2018
comment
О, и вы, вероятно, действительно хотите OR их вместе, а не AND. Очевидно, никакие пиксели не могут быть одновременно ‹75 и› 128.   -  person alkasm    schedule 06.04.2018


Ответы (2)


Здесь есть две разные возможности, и я не уверен, какая из них вам нужна, но обе они просты в реализации. Вы можете инвертировать (лучше сказать наоборот) оттенок радуги, просто используя 180 - hue. Или вы можете сместить цвет на 180 градусов, используя (hue + 90) % 180, как вы упомянули.

Поменять цвета вспять:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
rev_h = 180 - h
rev_hsv = cv2.merge([rev_h, s, v])
rev_img = cv2.cvtColor(rev_hsv, cv2.COLOR_HSV2BGR)

Сдвиг цветов:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
shift_h = (h + 90) % 180
shift_hsv = cv2.merge([shift_h, s, v])
shift_img = cv2.cvtColor(shift_hsv, cv2.COLOR_HSV2BGR)

Это идиоматические способы сделать это в OpenCV.

Теперь вы хотите сделать то же самое, что и выше, но только для некоторого замаскированного подмножества пикселей, которые удовлетворяют условию. Сделать это не так уж сложно; если вы хотите сместить некоторые замаскированные пиксели:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
h_mask = (h < 75) | (h > 128)
h[h_mask] = (h[h_mask] + 90) % 180
shift_hsv = cv2.merge([h, s, v])
shift_img = cv2.cvtColor(shift_hsv, cv2.COLOR_HSV2BGR)
person alkasm    schedule 06.04.2018
comment
Спасибо за такой ответ! Сдвиг цветов - это то, что мне нужно, но есть еще одна вещь - мне не нужно смещать значения в диапазоне (75,128) - person GodIsAnAstronaut; 06.04.2018
comment
Будьте осторожны - h + 90 будет переполнять диапазон байтов 256 в случаях, когда h уже больше 166. Возможно, вы захотите преобразовать в uint16 перед добавлением, иначе оттенок будет поврежден во время сдвига. - person JustAMartin; 18.09.2020

Канал оттенка имеет тип uint8, диапазон значений [0, 179]. Следовательно, при добавлении с большим числом или отрицательным числом Python возвращает число мусора. Вот моя база решения на основе кода изменения цвета @alkasm:

img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(img_hsv)
shift_h = random.randint(-50, 50)
h = ((h.astype('int16') + shift_h) % 180).astype('uint8')
shift_hsv = cv2.merge([h, s, v])

Для случайного изменения оттенка, насыщенности и значения. Сдвинуть базу каналов на @ bill-grates:

def shift_channel(c, amount):
   if amount > 0:
        lim = 255 - amount
        c[c >= lim] = 255
        c[c < lim] += amount
    elif amount < 0:
        amount = -amount
        lim = amount
        c[c <= lim] = 0
        c[c > lim] -= amount
    return c

rand_h, rand_s, rand_v = 50, 50, 50
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(img_hsv)
# Random shift hue
shift_h = random.randint(-rand_h, rand_h)
h = ((h.astype('int16') + shift_h) % 180).astype('uint8')
# Random shift saturation
shift_s = random.randint(-rand_s, rand_s)
s = shift_channel(s, shift_s)
# Random shift value
shift_v = random.randint(-rand_v, rand_v)
v = shift_channel(v, shift_v)
shift_hsv = cv2.merge([h, s, v])
print(shift_h, shift_s, shift_v)
img_rgb = cv2.cvtColor(shift_hsv, cv2.COLOR_HSV2RGB)
person Phan Van Hoai Duc    schedule 31.10.2019
comment
Хороший улов - мне было интересно, почему мой оттенок был искажен при переходе на красный. Но, конечно, 179 + 90 = 269, что слишком много для байтов uint8. Ваш .astype('int16') устранил проблему для меня. - person JustAMartin; 18.09.2020