Вы получаете цветные изображения, потому что вы вычитаете 2 цветных изображения, поэтому цвет, который вы получаете на каждом пикселе, представляет собой разницу на каждом канале (B, G и R) между обоими изображениями.
Чтобы выполнить вычитание фона, как отмечает dhanushka, самый простой вариант - использовать MOG2 и переслать ему свое фоновое изображение на несколько (500) кадров, чтобы он запомнил это как фон. MOG2 предназначен для изучения изменчивости цвета каждого пикселя с помощью гауссовской модели, поэтому, если вы подаете всегда одно и то же изображение, он не будет учиться этому. Во всяком случае, я думаю, что это должно работать для того, что вы собираетесь делать.
Хорошая особенность этого подхода заключается в том, что MOG2 позаботится о многих других вещах, таких как обновление модели с течением времени, работа с тенями и т. Д.
Другой вариант - реализовать собственный метод вычитания фона, как вы пытались это сделать.
Поэтому, если вы хотите проверить его, вам нужно преобразовать цветное изображение fgmask во что-то, что вы можете легко портировать и решить для каждого пикселя, является ли он фоном или передним планом. Простым вариантом было бы преобразовать его в оттенки серого, а затем применить простой порог, чем ниже порог, тем более «чувствителен» ваш метод вычитания (играть со значением порога), т.е.
...
# get foremask?
fgmask = frame - bg
gray_image = cv2.cvtColor(fgmask, cv2.COLOR_BGR2GRAY)
thresh = 20
im_bw = cv2.threshold(im_gray, thresh, 255, cv2.THRESH_BINARY)[1]
# filter kernel for denoising:
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
opening = cv2.morphologyEx(im_bw, cv2.MORPH_OPEN, kernel)
...