###### tags: `OpenCV`,`臉部識別`,`AR`,`dlib`
# OpenCV 專題實作篇part 1-擴增實境AR (ho~ho~ho~)
**擴增實境 (AR) – 旨在透過有限互動在真實世界檢視上新增數位元素。**
擴增實境類型
有兩種類型的擴增實境:標記式和無標記式。
標記式 AR 是使用影像辨識建立的,以識別編碼到 AR 裝置或應用程式中的物件。將物件做為參考點放置在視野範圍內時,它們可以幫助您的 AR 裝置確定攝影機的位置和方向。實現方式通常是透過將攝影機切換到灰階,並偵測標記,以將該標記與資訊庫中的所有其他標記進行比較。一旦裝置找到相符項目,它就會使用該資料以數學方式確定姿勢,並將 AR 影像放置在正確的位置。
無標記式 AR 更複雜,因為裝置沒有聚焦點。因此,裝置必須辨識出現在視野範圍內的項目。使用辨識演算法,該裝置將尋找色彩、圖樣和類似特徵來確定該物件是什麼,然後使用時間、加速計、GPS 和指南針資訊,裝置會自我定位,並使用攝影機將您想要的任何東西影像疊加在真實世界環境中。
擴增實境運作方式,AR有五個重要元件:
1. 人工智慧。大多數擴增實境解決方案都需要人工智慧 (AI) 才能運作,可讓使用者使用語音提示完成動作。AI 還可以幫助處理 AR 應用程式的資訊。
2. AR 軟體。這些是用於存取 AR 的工具和應用程式。一些企業可以建立自己的 AR 軟體形式。
3. 處理。需要處理能力才能讓 AR 技術正常運作,通常是利用裝置的內部作業系統。
4. 鏡頭。需要鏡頭或影像平台來檢視內容或影像。螢幕品質越好,影像就會越逼真。
5. 感應器。AR 系統需要消化有關其環境的資料,以使真實世界和數位世界保持一致。當攝影機擷取資訊時,它會透過軟體傳送資訊以進行處理。
**以下實作,在既有照片上,透過人臉辨識後,將帽子與鬍子加諸在照片上**
**step 1**
網路上找尋合適素材,這次找了聖誕帽,利用以下程式先將圖片做去背,以利後續
作為mask使用
(以下為這次素材)

```python!
import cv2
import numpy as np
#santa拼錯了!!
img_snata = cv2.imread("Santa_hat2.png")#素材照片
print(img_snata.shape)
imgCopy_hat = img_snata.copy()
mask_snata = np.zeros(img_snata.shape[:2], np.uint8)#這個先做一個空的mask
bgd_Model = np.zeros((1,65),np.float64)
fgd_Model = np.zeros((1,65),np.float64)
rect_snata= cv2.selectROI(img_snata)
cv2.destroyAllWindows()
for i in range(5,11,2): #可自行調整iterCount,看看不同結果(利用變數i去帶不同參數)
mask_snata = np.zeros(img_snata.shape[:2], np.uint8)
bgd_Model = np.zeros((1,65),np.float64)
fgd_Model = np.zeros((1,65),np.float64)
cv2.grabCut(img_snata,mask_snata,rect_snata,bgd_Model,fgd_Model,i,cv2.GC_INIT_WITH_RECT)
mask_santa2 = np.where((mask_snata==0)|(mask_snata==2),0,1).astype('uint8') #做一個只有0 & 1新的mask
img_copy_santa = img_snata.copy()
img_grabcut_santa = img_copy_santa*mask_santa2[:,:,np.newaxis]
cv2.imshow(f"Image{i}",img_grabcut_santa)
cv2.imwrite(f"Image{i}.png",img_grabcut_santa)
cv2.imshow("Original",img_snata)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
去背後,跑出來的成效如下圖,可以依據適合的Image去挑一張當mask

再加入鬍子的素材(這個沒有去背,程式會有些需差異)

尚未處理的圖片如下(出賣一下已經兩年前的自家人照片)

```python!
def shape_to_np(dlib_shape, dtype="int"):
# 先做出一個np.array等著裝座標
# 資料型別必須是int
# num_parts代表有幾個特徵點
# 因為每個座標只有x y,所以設2
coordinates = np.zeros((dlib_shape.num_parts, 2), dtype=dtype)#做格子
# for下去把每一個座標裝出來
for i in range(0, dlib_shape.num_parts):
coordinates[i] = (dlib_shape.part(i).x, dlib_shape.part(i).y)
return coordinates
```
**主程式**
```python!
import cv2
import numpy as np
import dlib
# Load image:
image_ori = cv2.imread('snowman.jpg')
image = cv2.resize(image_ori, (0,0), fx=0.3, fy=0.3)
#載入mask圖片
face_mask= cv2.imread('beard.png')
hat_mask= cv2.imread('Image9.png')
gray = cv2.cvtColor(image , cv2.COLOR_BGR2GRAY)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
rects = detector(gray, 1)
#開始對找到的臉做landmark分析
for (i, rect) in enumerate(rects):
shape = predictor(gray, rect)
shape = shape_to_np(shape)
#找出臉部定位點
#landmarks---[30, 35]---下鼻
#NL :botton_nose_left , NR :botton_nose_right
#ML mouth_left MR mouth_Right
x_NL,y_NL=shape[31]
x_NR,y_NR=shape[35]
x_ML,y_ML=shape[48]
x_MR,y_MR=shape[54]
x_Eyebrow_L,y_Eyebrow_L=shape[19] #landmarks---[17, 21]---左眉,取中間值
x_Eyebrow_R,y_Eyebrow_R=shape[24] #landmarks---[22, 26]---右眉,取中間值
#找出鬍子位置 後面的數字要自行調整
h=int((y_ML-y_NL)*0.9)
w=int((x_MR-x_ML)*1.5)
x=x_NL-12 #鬍子左右設定
y=y_NL
#找出帽子位置 後面的數字要自行調整
h2 = int((y_NL-y_Eyebrow_L)*3.0) #帽子高度: 左下鼻子的y-眉毛的y
w2 = int((x_Eyebrow_R-x_Eyebrow_L)*3.4) #帽子寬度: 右眉毛的x-左眉毛的x
x2 = x_Eyebrow_L-30
y2 = y_Eyebrow_L-100
#確定目標區域
frame_roi = image[y:y+h, x:x+w]
frame_roi2 = image[y2:y2+h2, x2:x2+w2]
#調整鬍子&帽子大小
face_mask_beard = cv2.resize(face_mask, (w,h),interpolation=cv2.INTER_AREA)
smallhat_mask= cv2.resize(hat_mask, (w2,h2), interpolation=cv2.INTER_AREA)
#做出鬍子&帽子的遮罩
gray_mask1 = cv2.cvtColor(face_mask_beard, cv2.COLOR_BGR2GRAY)
ret1, mask1 = cv2.threshold(gray_mask1, 180, 255, cv2.THRESH_BINARY) #mask1 鬍子是白色(255)的
gray_mask2 = cv2.cvtColor(smallhat_mask, cv2.COLOR_BGR2GRAY)
ret2, mask2 = cv2.threshold(gray_mask2, 150, 255, cv2.THRESH_TRUNC) #(img,127,255,cv2.THRESH_TRUNC)#大於127的就變成127,其餘不變
ret2, mask3 = cv2.threshold(mask2, 50, 255, cv2.THRESH_BINARY)
#依照遮罩,把鬍子挖出來
masked_face = cv2.bitwise_and(face_mask_beard, face_mask_beard, mask=mask1) #白色老公公鬍子+黑背景照片
masked_face3 = cv2.bitwise_and(smallhat_mask, smallhat_mask, mask=mask3)
#依照遮罩,把ROI的地方挖出洞來
mask1_inv = cv2.bitwise_not(mask1) ##鬍子部分為黑色的(用mask1做顏色反轉)
masked_frame = cv2.bitwise_and(frame_roi, frame_roi, mask=mask1_inv)#ROI鬍子部分是黑色的
mask3_inv = cv2.bitwise_not(mask3) #bitwise_not黑會轉白 白會轉黑
masked_frame3 = cv2.bitwise_and(frame_roi2, frame_roi2, mask=mask3_inv)
#合體
image[y:y+h, x:x+w] = cv2.add(masked_face, masked_frame)
image[y2:y2+h2, x2:x2+w2] = cv2.add(masked_face3, masked_frame3)
cv2.imshow('Combind santa', image )
cv2.imwrite('Combind santa.png', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
大功告成!!
結果我家弟弟的臉太側面了,dlib不好辨識出來,只抓到爸爸跟姊姊的臉!!
