Каждое изображение имеет три канала: R, G, B, то есть красный, зеленый и синий, которые используются для определения значения пикселя в любой точке изображения, где значение красного, зеленого или синего находится в диапазоне от 0 до 255.

Например: значение пикселя [255, 0, 0] будет полностью КРАСНЫМ, а [255, 255, 0] будет сочетанием КРАСНОГО и ЗЕЛЕНОГО, что даст ЖЕЛТЫЙ цвет.

Но если изображение читается с использованием OpenCV, оно дает изображение в формате BGR, то есть [255, 0, 0] будет СИНИМ и так далее.

Установка OpenCV

OpenCV - это библиотека с открытым исходным кодом для обработки изображений на языке Python или C.

Для Python OpenCV можно загрузить с помощью pip install opencv-python.

Чтение изображения в OpenCV

Любое изображение можно прочитать в opencv с помощью команды cv2.imread(). Однако OpenCV пока не поддерживает изображения HEIC, возможно, вам придется использовать другую библиотеку, например Pillow, для чтения изображений HEIC (или сначала конвертировать их в .JPEG).

import cv2
image = cv2.imread(‘image.jpg’)

После прочтения изображения его при необходимости можно преобразовать в формат RGB из BGR с помощью команды cv2.cvtColor().

image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Оверлеи

Изображения - это не что иное, как набор значений пикселей, хранящихся в матричном формате. Значение любого пикселя можно изменить независимо от других.

Допустим, есть изображение

Прочтите изображение с помощью opencv:

image_1 = cv2.imread(‘image_1.jpg’)
print(image_1)

Это дает набор значений пикселей в матричной форме.

array([[[107, 108, 106],
[107, 108, 106],
[107, 108, 106],
…,
[ 77, 78, 76],
[ 77, 78, 76],
[ 76, 77, 75]],
…,
[[ 93, 88, 87],
[ 93, 88, 87],
[ 92, 87, 86],
…,
[ 52, 62, 62],
[ 52, 62, 62],
[ 52, 62, 62]]], dtype=uint8)

Если вы просто измените значения пикселей в определенной области изображений, скажем, [0, 0, 0], эта область изображения станет ЧЕРНОЙ, поскольку это значения пикселей ЧЕРНОГО цвета. Точно так же, если вы измените значения пикселей на [255, 0, 0], эта область станет СИНЕЙ (OpenCV считывает изображения в формате BGR).

image_1[50: 100, 50:100] = [255, 0, 0]

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

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

Это можно сделать с помощью функции cv2.resize().

image_2 = cv2.imread(‘image_2.jpg’)
resized_image_2 = cv2.resize(image_2, dsize=(100, 100))

Здесь dsize принимает размеры, до которых должно быть изменено изображение.

Теперь второе изображение можно наложить поверх первого.

image_1[50:150, 50:150] = resized_image_2

Наложение изображений PNG

В отличие от изображений JPEG, изображения PNG (переносимая сетевая графика) также могут иметь в себе 4-й канал, который определяет АЛЬФА (непрозрачность) в данном пикселе.

OpenCV читает PNG так же, как JPEG (то есть с 3 каналами), если не указано иное.

Чтобы прочитать изображение PNG с его значениями Alpha, нам нужно указать флаг cv2.IMREAD_UNCHANGED при чтении изображения.

Теперь считываемое изображение имеет 4 канала: BGRA.

image_3 = cv2.imread(‘image_3.png’, cv2.IMREAD_UNCHANGED)
print(image_3)
array([[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
…
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
…
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
…
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]], dtype=uint8)

(** Примечание. ** Все напечатанные значения равны нулю, поскольку начало и конец изображения являются пустыми.

Однако у этого изображения 4 канала, а у нашего изображения JPEG только 3 канала, поэтому эти значения нельзя просто заменить.

Нам нужно добавить фиктивный канал в наше изображение JPEG.

Для этого мы будем использовать numpy. Его можно установить с помощью pip install numpy.

numpy предлагает функцию numpy.dstack() для складывания значений по глубине.

Во-первых, нам нужен фиктивный массив того же размера, что и изображение.

Чтобы создать фиктивный канал, мы можем использовать функцию numpy.ones() для создания массива из них.

import numpy as np
ones = np.ones((image_1.shape[0], image_1.shape[1]))*255
image_1 = np.dstack([image_1, ones])

Мы умножаем массив единиц на 255, потому что значение альфа-канала также существует между 0–255.

Теперь вы можете заменить значения пикселей изображения на изображение PNG.

image_1[150:250, 150:250] = image_3

Однако это не даст желаемого результата, так как мы также меняем значение альфа-канала на ноль.

Нам нужно заменить только те значения пикселей, которые имеют ненулевое значение.

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

Итак, есть способ получше.

Вы можете взять альфа-значения изображения, которое будет наложено.

alpha_image_3 = image_3[:, :, 3] / 255.0

Мы делим значения пикселей на 255,0, чтобы сохранить значения между 0–1.

Сумма альфа image_1 и image_3 должна быть равна 255.

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

alpha_image = 1 — alpha_image_3

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

for c in range(0, 3):
    image_1[150:250, 150:250, c] = ((alpha_image*image_1[150:250,      150:250, c]) + (alpha_image_3*image_3[:, :, c]))

Вуаля! Изображение теперь наложено поверх другого. эз пз :)