使用OpenCV進行簡單的人像分割與合成
阿新 • • 發佈:2021-01-15
# 影象合成
## 實現思路
通過背景建模的方法,對源影象中的動態人物前景進行分割,再將目標影象作為背景,進行合成操作,獲得一個可用的合成影像。
實現步驟如下。
### 使用BackgroundSubtractorMOG2進行背景分割
BackgroundSubtractorMOG2是一個以高斯混合模型為基礎的背景前景分割演算法,
#### 混合高斯模型
![](https://www.zhihu.com/equation?tex=p%28x%29%3D%5Csum_%7Bi%3D1%7D%5E%7BK%7D%7B%5Cphi_%7Bi%7D%7D%5Cfrac%7B1%7D%7B%5Csqrt%7B2%5Csigma_%7Bi%7D%5E%7B2%7D%5Cpi%7D%7De%5E%7B-%5Cfrac%7B%28x-%5Cmu_%7Bi%7D%29%5E%7B2%7D%7D%7B2%5Csigma_%7Bi%7D%5E%7B2%7D%7D%7D)
分佈概率是K個高斯分佈的和,每個高斯分佈有屬於自己的 $\mu$ 和 $\sigma$ 引數,以及對應的權重引數,權重值必須為正數,所有權重的和必須等於1,以確保公式給出數值是合理的概率密度值。換句話說如果我們把該公式對應的輸入空間合併起來,結果將等於1。
回到原演算法,它的一個特點是它為每一個畫素選擇一個合適數目的高斯分佈。基於高斯模型的期望和標準差來判斷混合高斯模型模型中的哪個高斯模型更有可能對應這個畫素點,如果不符合就會被判定為前景。
### 使用人像識別填充面部資訊
#### 建立級聯分類器
```python
face_cascade = cv2.CascadeClassifier()
face_cascade.load(
'/usr/local/anaconda3/envs/OpenCV/lib/python3.8/site-packages/cv2/data/haarcascade_frontalface_default.xml')
```
使用OpenCV自帶的級聯分類器,載入OpenCV的基礎人像識別資料。
#### 識別源影象中的人像
```python
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
```
### 使用形態學填充分割出來的前景
```python
# 形態學開運算去噪點
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
for i in range(15):
fgmask = cv2.dilate(fgmask, kernel, iterations=1)
```
通過開操作去掉前景影象陣列中的噪點,然後重複進行膨脹,填充前景輪廓。
### 將人像與目標背景進行合成
```python
def resolve(o_img, mask, faces):
if len(faces) == 0:
return
(x, y, w, h) = faces[0]
rgb_mask_front = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
rgb_mask_front = cv2.bitwise_not(rgb_mask_front)
cv2.circle(rgb_mask_front, (int(x + w / 2), int(y + h / 2)), int((w + h) / 4), (0, 0, 0), thickness=-1)
o_img = cv2.subtract(o_img, rgb_mask_front)
return o_img
```
將分割出來的部分取反再與源影象進行減操作,相當於用一個Mask從原圖中摳出一部分。
再與背景進行加操作
```python
out = resolve(frame, fgmask, faces)
out = cv2.add(out, c_frame)
```
## 程式碼實現
```python
import numpy as np
import cv2
import os
# 經典的測試視訊
camera = cv2.VideoCapture('./source/background_test2.avi')
cap = cv2.VideoCapture('./source/camera_test2.avi')
face_cascade = cv2.CascadeClassifier()
face_cascade.load(
os.getcwd()+'/source/haarcascade_frontalface_default.xml')
# 形態學操作需要使用
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# 建立混合高斯模型用於背景建模
fgbg = cv2.createBackgroundSubtractorMOG2(detectShadows=False)
def resolve(o_img, mask, faces):
if len(faces) == 0:
return
(x, y, w, h) = faces[0]
rgb_mask_front = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
rgb_mask_front = cv2.bitwise_not(rgb_mask_front)
cv2.circle(rgb_mask_front, (int(x + w / 2), int(y + h / 2)), int((w + h) / 4), (0, 0, 0), thickness=-1)
o_img = cv2.subtract(o_img, rgb_mask_front)
return o_img
while True:
ret, frame = cap.read()
c_ret, c_frame = camera.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
fgmask = fgbg.apply(frame)
# 形態學開運算去噪點
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
gray_camera = cv2.cvtColor(c_frame, cv2.COLOR_BGR2GRAY)
for i in range(15):
fgmask = cv2.dilate(fgmask, kernel, iterations=1)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
out = resolve(frame, fgmask, faces)
out = cv2.add(out, c_frame)
cv2.imshow('Result', out)
cv2.imshow('Mask', fgmask)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
out.release()
camera.release()
cap.release()
cv2.destroyAllWindo