###### tags: `OpenCV`,`去背`,`圖像分割`,`grabcut`,`bitwise` # OpenCV 專題實作篇part 3-"失敗的麵"現正熱映中(惡搞小孩又來惹) 目標:讓我家小孩去當演員 歡迎,我們這次的主角,失敗的麵主角~~~ ![](https://i.imgur.com/HEyf8DS.jpg) ```python= import cv2 import numpy as np img_le = cv2.imread("spider_lele.jpg") img = cv2.resize(img_le, (0,0), fx=0.2, fy=0.2)#圖片過大,縮小一下 print(img.shape) mask = np.zeros(img.shape[:2], np.uint8)#這個先做一個空的mask bgdModel = np.zeros((1,65),np.float64) fgdModel = np.zeros((1,65),np.float64) rect = cv2.selectROI(img)#選取失敗的麵主角 cv2.destroyAllWindows() for i in range(3,10,2): #可以透過這個去調整,看哪個效果好 mask = np.zeros(img.shape[:2], np.uint8) bgdModel = np.zeros((1,65),np.float64) fgdModel = np.zeros((1,65),np.float64) cv2.grabCut(img,mask,rect,bgdModel,fgdModel,i,cv2.GC_INIT_WITH_RECT) mask2 = np.where((mask==0)|(mask==2),0,1).astype('uint8') # print(np.unique(mask2)) img_copy = img.copy() img_copy = img_copy*mask2[:,:,np.newaxis]#原本mask為2d結構,現在把資料格式變成3d結構 cv2.imshow(f"Image{i}_spider",img_copy) cv2.imwrite(f"Image{i}_spider.png",img_copy) cv2.waitKey(0) cv2.destroyAllWindows() ``` run完結果效果也是沒有很好,只好再次出動一次筆刷功能啦!! ![](https://i.imgur.com/YACaZXz.jpg) ```python= import cv2 import numpy as np img = cv2.imread("Image5_spider.png") imgCopy = img.copy() mask = np.zeros(img.shape[:2], np.uint8) bgdModel = np.zeros((1,65),np.float64) fgdModel = np.zeros((1,65),np.float64) rect = cv2.selectROI(img) x,y,w,h = rect cv2.grabCut(img,mask,rect,bgdModel,fgdModel,3,cv2.GC_INIT_WITH_RECT) mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8') cv2.imshow("Mask2",mask2*255) cv2.waitKey(0) cv2.destroyAllWindows() ``` 可以稍微看一下ROI的結果 ![](https://i.imgur.com/n9TbmXm.jpg) ```python= #將mask還原為3d結構 imgResult = img*mask2[:,:,np.newaxis] cv2.imshow("Image",imgResult) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/8sbW7Zg.jpg) ```python= #把圖各準備一份 img_mask = img.copy() mask2 = mask2*255 mask_copy = mask2.copy() ``` 準備筆刷工具 ```python class Sketcher: def __init__(self, windowname, dests, colors_func): #視窗名稱 self.windowname = windowname #顯示的圖片目標 #會塞兩張圖進來 顯示cut圖和mask圖 self.dests = dests #顯示的顏色 self.colors_func = colors_func #一啟動就要顯示結果 self.show() #要記錄滑鼠座標位置 self.prev_pt = None #把滑鼠的callback function裝上來 #windowname是設定要把function裝在哪個視窗 #on_mouse是下面設計的function,這個function是要給cv2.setMouseCallback呼叫的,每逢有滑鼠訊號,cv2就會把參數傳給我們的on_mouse cv2.setMouseCallback(self.windowname, self.on_mouse) def show(self): #就是顯示圖 #會塞兩張圖進來 顯示cut圖和mask圖 cv2.imshow(self.windowname , self.dests[0]) cv2.imshow(self.windowname + ": mask" , self.dests[1]) # onMouse function for Mouse Handling # 處理滑鼠動作的指令 def on_mouse(self, event, x, y, flags, param): #point座標 pt = (x, y) #當滑鼠剛按下去,就先記錄一下這個點的座標,代表畫線任務開始,只要prev_pt不是None 下面的if判斷式就會進行 if event == cv2.EVENT_LBUTTONDOWN: self.prev_pt = pt elif event == cv2.EVENT_LBUTTONUP: #鬆開就忘記座標,歸0,那下面線就畫不出來了,代表這個畫線任務結束 self.prev_pt = None #self.prev_pt如果沒有歸零,就代表是同一個畫線任務仍在進行中 #CV_EVENT_FLAG_LBUTTON 的意思是 有左鍵拖曳的事件發生 #cv2.line(影像, 開始座標, 結束座標, 顏色, 線條寬度) if self.prev_pt and cv2.EVENT_FLAG_LBUTTON: #針對兩個圖分別處理標示 for dst, color in zip(self.dests, self.colors_func()): #((0,0,255), 0) #隨著你畫線,會開始在兩張圖裡面開始標註color,第一張圖就會配你設計的(0,0,255),如果第二張圖就會用後面那個0了 cv2.line(dst, self.prev_pt, pt, color, 5) #每次畫了線就更新一下起點,那你就會畫出一條線,如果這句刪掉會怎樣?就會每次原點出發 self.prev_pt = pt self.show() ``` ```python while True: #把蒐集到鍵盤的訊號存起來,下面就開始根據不同的訊號,開始不同的處理 ch = cv2.waitKey() #Quit #按esc if ch == 27: print("exiting...") #存照片,不接受中文名稱 cv2.imwrite("spyderman_cut.png",img_mask) cv2.imwrite("spyderman_cutmask.png",mask2) break # 按r的時候Reset elif ch == ord('r'): print("重新開始...") img_mask = img.copy() mask2 = mask_copy.copy() #處理成0或1 mask2 = mask2//255 #切割、重分類 #沒有設定rect,改用None cv2.grabCut(img,mask2,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) mask2 = np.where((mask2==2)|(mask2==0),0,1).astype('uint8') #準備兩個要顯示的圖 img_mask = img*mask2[:,:,np.newaxis] mask2 = mask2*255 #'image'是sketcher show的時候,視窗的名稱 # [img_mask, mask2] 是等一下要顯示的cut圖和mask圖 # lambda是等一下sketch顯示的顏色 sketch = Sketcher('image', [img_mask, mask2], lambda : ((255,0,0), 255)) sketch.show() # 按了b,表示改成背景選取 elif ch == ord('b'): print("處理背景...") sketch = Sketcher('image', [img_mask, mask2], lambda : ((0,0,255), 0)) #這裡lambda的設定就是(img_mask的顏色,mask2的顏色),因為這是畫背景,所以mask2的顏色是0 sketch.show() # 按了f,表示改成前景選取 elif ch == ord('f'): print("處理前景...") sketch = Sketcher('image', [img_mask, mask2], lambda : ((255,0,0), 255)) #這裡lambda的設定就是(img_mask的顏色,mask2的顏色),因為這是畫前景,所以mask2的顏色是1 sketch.show() else: # print("進行 grabcut...") #處理成0或1 mask2 = mask2//255 #沒有設定rect,改用None cv2.grabCut(img,mask2,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) mask2 = np.where((mask2==2)|(mask2==0),0,1).astype('uint8') img_mask = img*mask2[:,:,np.newaxis] mask2 = mask2*255 # print("處理好了...前台看看...") sketch = Sketcher('image', [img_mask, mask2], lambda : ((255,0,0), 255)) sketch.show() cv2.destroyAllWindows() ``` 可以看一下程式run的過程,利用鍵盤按"b"進行背景處理,這時會觸動滑鼠按鍵function,滑鼠塗紅色的區域就是要去背的部分,可以按一下空白鍵,會在顯示修改過後的結果! ![](https://i.imgur.com/idWGYZu.jpg) ![](https://i.imgur.com/fP43Az6.jpg) 結果滿意後記得按下esc鍵,才會進行修改後檔案的儲存功能喔!! 已儲存至資料夾中啦!! ![](https://i.imgur.com/8PbgsHE.jpg) 緊接著就來找張電影廣告海報啦!! 就決定是你了(丟寶貝球)~~~~ 去吧~~~ ![](https://i.imgur.com/EN0nnTN.jpg) ```python #lele movie star import cv2 import numpy as np # Load image: image = cv2.imread('spiderman.jpg') face_mask= cv2.imread('spyderman_cut.png') gray = cv2.cvtColor(image , cv2.COLOR_BGR2GRAY) rect = cv2.selectROI(gray) x,y,w,h = rect #確定目標區域 frame_roi = image[y:y+h, x:x+w] #調整lele大小 face_mask_lele = cv2.resize(face_mask, (w,h),interpolation=cv2.INTER_AREA) #做出lele的遮罩 gray_mask = cv2.cvtColor(face_mask_lele, cv2.COLOR_BGR2GRAY) ret, mask1 = cv2.threshold(gray_mask, 2, 255, cv2.THRESH_BINARY) #threshold修改為2(接近黑色的地方) #依照遮罩,把lele挖出來 masked_face = cv2.bitwise_and(face_mask_lele, face_mask_lele, mask=mask1) #依照遮罩,把ROI的地方挖出洞來 mask1_inv = cv2.bitwise_not(mask1) #用mask1做顏色反轉 masked_frame = cv2.bitwise_and(frame_roi, frame_roi, mask=mask1_inv) #合體 image[y:y+h, x:x+w] = cv2.add(masked_face, masked_frame) cv2.imshow('Spider_image', image ) cv2.imwrite('Spider_lele2.png', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 來囉來囉! ![](https://i.imgur.com/rWsNrlj.jpg) 這次腳在水面上,決定再將"spyderman_cut.png",照片黑色的部分再修改少一些,這樣人物會再大一些,角度也可以在微調一下,再重新run一次程式! ![](https://i.imgur.com/RgCOmxg.png) 重run後的效果更好了!! **電影結束囉~謝謝您的收看!!**