###### 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使用 (以下為這次素材) ![](https://i.imgur.com/PTTHR1K.png) ```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 ![](https://i.imgur.com/HHWQEsY.jpg) 再加入鬍子的素材(這個沒有去背,程式會有些需差異) ![](https://i.imgur.com/3HqSXMv.png) 尚未處理的圖片如下(出賣一下已經兩年前的自家人照片) ![](https://i.imgur.com/RxYlJOm.jpg) ```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不好辨識出來,只抓到爸爸跟姊姊的臉!! ![](https://i.imgur.com/8sIpJ5j.png)