OpenCV - применить маску к цветному изображению
Как применить маску к цветному изображению в последней привязке python (cv2)? В предыдущем связывании с python самым простым способом было использовать cv.Copy
например.
cv.Copy(dst, src, mask)
Но эта функция недоступна в привязке cv2. Есть ли способ обхода без использования шаблона?
Ответы
Ответ 1
Здесь вы можете использовать функцию cv2.bitwise_and
, если у вас уже есть изображение маски.
Для проверки кода ниже:
img = cv2.imread('lena.jpg')
mask = cv2.imread('mask.png',0)
res = cv2.bitwise_and(img,img,mask = mask)
Выход будет выглядеть следующим образом для изображения lena и для прямоугольной маски.
![enter image description here]()
Ответ 2
Ну, вот решение, если вы хотите, чтобы фон был отличным от сплошного черного цвета. Нам нужно только инвертировать маску и применить ее в фоновом изображении того же размера, а затем объединить фон и передний план. Про это решение состоит в том, что фон может быть любым (даже другим изображением).
Этот пример изменен из Hough Circle Transform. Первое изображение - это логотип OpenCV, второй - оригинальная маска, третий - фон + передний план.
![применить маску и получить настраиваемый фон]()
# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghcircles/py_houghcircles.html
import cv2
import numpy as np
# load the image
img = cv2.imread('E:\\FOTOS\\opencv\\opencv_logo.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# detect circles
gray = cv2.medianBlur(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY), 5)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=50, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))
# draw mask
mask = np.full((img.shape[0], img.shape[1]), 0, dtype=np.uint8) # mask is only
for i in circles[0, :]:
cv2.circle(mask, (i[0], i[1]), i[2], (255, 255, 255), -1)
# get first masked value (foreground)
fg = cv2.bitwise_or(img, img, mask=mask)
# get second masked value (background) mask must be inverted
mask = cv2.bitwise_not(mask)
background = np.full(img.shape, 255, dtype=np.uint8)
bk = cv2.bitwise_or(background, background, mask=mask)
# combine foreground+background
final = cv2.bitwise_or(fg, bk)
Примечание. Лучше использовать opencv-методы, потому что они оптимизированы.
Ответ 3
import cv2 as cv
im_color = cv.imread("lena.png", cv.IMREAD_COLOR)
im_gray = cv.cvtColor(im_color, cv.COLOR_BGR2GRAY)
На этом этапе у вас есть цветное и серое изображение. Мы имеем дело с изображениями 8-bit
, uint8
здесь. Это означает, что изображения могут иметь значения пикселей в диапазоне [0, 255]
, а значения должны быть целыми числами.
Позвольте сделать бинарную операцию порога. Создает черно-белое изображение в маске. Черные регионы имеют значение 0
, а белые - 255
_, mask = cv.threshold(im_gray, thresh=180, maxval=255, type=cv.THRESH_BINARY)
im_thresh_gray = cv.bitwise_and(im_gray, mask)
Маска видна ниже слева. Изображение справа это результат применения операции bitwise_and
между серым изображением и маской. То, что произошло, это то, что пространственные местоположения, где маска имела нулевое значение пикселя (черный), стали нулевым значением пикселя в конечном изображении. В тех местах, где маска имела значение пикселя 255 (белый), полученное изображение сохранило свое первоначальное значение серого.
![left-mask,right-bitwise_and_with_mask]()
Чтобы применить эту маску к нашему исходному цветному изображению, нам нужно преобразовать маску в 3-канальное изображение, поскольку исходное цветное изображение - это 3-канальное изображение.
mask3 = cv.cvtColor(mask, cv.COLOR_GRAY2BGR) # 3 channel mask
Затем мы можем применить эту маску к нашему исходному цветному изображению, используя ту же функцию bitwise_and
.
im_thresh_color = cv.bitwise_and(im_color, mask3)
mask3
из кода - изображение внизу слева, а im_thresh_color
- справа.
![left-mask-3channel,right-bitwise_and_with_3channel-mask]()
Вы можете построить результаты и убедиться сами.
cv.imshow("original image", im_color)
cv.imshow("binary mask", mask)
cv.imshow("3 channel mask", mask3)
cv.imshow("im_thresh_gray", im_thresh_gray)
cv.imshow("im_thresh_color", im_thresh_color)
cv.waitKey(0)
Исходное изображение - lenacolor.png
, которое я нашел здесь.
Ответ 4
Другие описанные методы предполагают двоичную маску. Если вы хотите использовать однозначное одноканальное изображение в оттенках серого в качестве маски (например, из альфа-канала), вы можете развернуть его на три канала, а затем использовать для интерполяции:
assert len(mask.shape) == 2 and issubclass(mask.dtype.type, np.floating)
assert len(foreground_rgb.shape) == 3
assert len(background_rgb.shape) == 3
alpha3 = np.stack([mask]*3, axis=2)
blended = alpha3 * foreground_rgb + (1. - alpha3) * background_rgb
Обратите внимание, что mask
должен находиться в диапазоне 0..1
для успешной операции. Также предполагается, что 1.0
кодирует сохранение только переднего плана, а 0.0
означает сохранение только фона.
Если маска может иметь форму (h, w, 1)
, это помогает:
alpha3 = np.squeeze(np.stack([np.atleast_3d(mask)]*3, axis=2))
Здесь np.atleast_3d(mask)
делает маску (h, w, 1)
, если она (h, w)
и np.squeeze(...)
изменяет результат от (h, w, 3, 1)
до (h, w, 3)
.
Ответ 5
Ответ, данный Абидом Рахманом К, не совсем корректен. Я также попробовал его и нашел очень полезным, но застрял.
Вот как я копирую изображение с заданной маской.
x, y = np.where(mask!=0)
pts = zip(x, y)
# Assuming dst and src are of same sizes
for pt in pts:
dst[pt] = src[pt]
Это немного медленно, но дает правильные результаты.
EDIT:
Питонический путь.
idx = (mask!=0)
dst[idx] = src[idx]