-
Notifications
You must be signed in to change notification settings - Fork 29
Для разработчиков: Функция OpenCV LUT
Интересный способ вычислений над uint8-матрицами используется в библиотеке Albumentations для изменения параметров изображения (яркость, контраст, оттенок, насыщенность). Поскольку способ простой и красивый, используется скорее всего в других местах, и в итоге перкочевал к нам, здесь будет его объяснение. В лакмусе в соответствии с данным подходом реализован код в файле image_adjustments.py
Предположим, надо провести elementwise-вычисления над большой uint8-матрицей с изображением. Вычисления эти по природе своей выполняются над числами с плавающей запятой, но итоговая матрица всё равно должна содержать целые числа от 0 до 255, как того предполагает формат RGB, BGR, HVS в OpenCV и тому подобные. Если применять формулу к каждому элементу по отдельности, это будет неэффективно, во-первых, из-за большого количества вычислений, и, во-вторых, из-за таскания больших матриц из uint8 в float32-формат и обратно. При этом, если матрица размера, скажем, 800х1333х3, то количество элементов в ней значительно превышает количество их возможных значений. Формула тем временем применяется к каждому элементу независимо, а отображение out = formula(in) - однозначное, так что многие вычисления будут просто дублироваться. Этого можно избежать, если для каждого из 256 возможных входных элементов сразу вычислить выходное значение, а потом просто применить маппинг по такому короткому словарю к элементам большой матрицы. Этим и занимается функция OpenCV cv2.LUT:
The function LUT fills the output array with values from the look-up table. Indices of the entries are taken from the input array.
В коде это может выглядеть приблизительно так:
import cv2
import numpy as np
max_value = 255
lookup_table = np.arange(0, max_value + 1).astype("float32")
lookup_table += brightness_delta * 255
lookup_table = np.clip(lookup_table, 0, 255).astype(np.uint8)
image = cv2.LUT(image, lookup_table)
На что ещё следует обратить внимание, так это на количество каналов в lookup_table. Оно должно либо равняться 1, как в примере выше, либо соответствовать количеству каналов изображения. В первом случае одно и то же преобразование будет применяться ко всем каналам изображения. Если же нужно проводить вычисления отдельно для каждого канала, то lookup_table должна содержать преобразования для каждого из них. Получить такой многомерный словарь можно при помощи функции numpy dstack:
num_channels = 3
uint_range = np.arange(0, max_value + 1)
channels_lookups = np.tile(uint_range, (num_channels, 1))
lookup_table = np.dstack(channels_lookups)
Здесь np.tile повторяет один и тот же массив num_channels раз, а dstack конкатенирует их вдоль оси "вглубину", формируя тензор размерности (1, 256, 3). Схематично это выглядит так:
That's it.
Last update | Обновлено: 15.05.2020
The Lacmus project is distributed under the GNU license. All rights reserved. | Проект Lacmus распостраняется под лицензией GNU. Все права зищищены.