Примечание. Я объясню общий принцип и приведу пример реализации на Python, так как у меня не настроена среда разработки Android. Должно быть довольно просто перенести это на Java. Не стесняйтесь публиковать свой код как отдельный ответ.
Вам нужно сделать что-то похожее на то, что делает операция addWeighted
, то есть операция
Однако в вашем случае должна быть матрица (т.е. нам нужен другой коэффициент смешивания для каждого пикселя).
Примеры изображений
Давайте используем несколько примеров изображений, чтобы проиллюстрировать это. Мы можем использовать изображение Лены в качестве образца лица:
Это изображение как наложение с прозрачностью:
И это изображение как наложение без прозрачности:
Матрица смешивания
Чтобы получить альфа-матрицу, мы можем либо определить маски переднего плана (наложение) и фона (лицо) с помощью пороговой обработки, либо использовать альфа-канал из входного изображения, если он доступен.
Это полезно выполнять для изображений с плавающей запятой со значениями в диапазоне 0,0 .. 1,0. Затем мы можем выразить отношение между двумя масками как
foreground_mask = 1.0 - background_mask
то есть две маски, сложенные вместе, дают все единицы.
Для накладываемого изображения в формате RGBA мы получаем следующие маски переднего плана и фона:
Когда мы используем пороговое значение, эрозию и размытие в случае формата RGB, мы получаем следующие маски переднего плана и фона:
Взвешенная сумма
Теперь мы можем вычислить две взвешенные части:
foreground_part = overlay_image * foreground_mask
background_part = face_image * background_mask
Для наложения RGBA части переднего плана и фона выглядят следующим образом:
А для наложения RGB части переднего плана и фона выглядят так:
И, наконец, сложите их вместе и преобразуйте изображение обратно в 8-битные целые числа в диапазоне 0-255.
Результат операций выглядит следующим образом (наложение RGBA и RGB соответственно):
Пример кода — наложение RGB
import numpy as np
import cv2
# ==============================================================================
def blend_non_transparent(face_img, overlay_img):
# Let's find a mask covering all the non-black (foreground) pixels
# NB: We need to do this on grayscale version of the image
gray_overlay = cv2.cvtColor(overlay_img, cv2.COLOR_BGR2GRAY)
overlay_mask = cv2.threshold(gray_overlay, 1, 255, cv2.THRESH_BINARY)[1]
# Let's shrink and blur it a little to make the transitions smoother...
overlay_mask = cv2.erode(overlay_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
overlay_mask = cv2.blur(overlay_mask, (3, 3))
# And the inverse mask, that covers all the black (background) pixels
background_mask = 255 - overlay_mask
# Turn the masks into three channel, so we can use them as weights
overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)
# Create a masked out face image, and masked out overlay
# We convert the images to floating point in range 0.0 - 1.0
face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))
# And finally just add them together, and rescale it back to an 8bit integer image
return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))
# ==============================================================================
# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_img = cv2.imread("overlay.png", -1)
result_1 = blend_non_transparent(face_img, overlay_img)
cv2.imwrite("merged.png", result_1)
Пример кода — наложение RGBA
import numpy as np
import cv2
# ==============================================================================
def blend_transparent(face_img, overlay_t_img):
# Split out the transparency mask from the colour info
overlay_img = overlay_t_img[:,:,:3] # Grab the BRG planes
overlay_mask = overlay_t_img[:,:,3:] # And the alpha plane
# Again calculate the inverse mask
background_mask = 255 - overlay_mask
# Turn the masks into three channel, so we can use them as weights
overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)
# Create a masked out face image, and masked out overlay
# We convert the images to floating point in range 0.0 - 1.0
face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))
# And finally just add them together, and rescale it back to an 8bit integer image
return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))
# ==============================================================================
# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_t_img = cv2.imread("overlay_transparent.png", -1) # Load with transparency
result_2 = blend_transparent(face_img, overlay_t_img)
cv2.imwrite("merged_transparent.png", result_2)
person
Dan Mašek
schedule
12.05.2016