一、背景
前几天朋友圈@微信要圣诞帽的事被刷屏了,正好研究深度学习看到了opencv这里,并且大早上看到了知乎日报源码都贴出来了,那么自实现下就水到渠成了。
二、环境
系统:mac osx 10.13.2
开发环境:python 3.6.3
opencv 3.4.0
dlib 19.8.99
mac上装环境极其痛苦,不过这里还是不说了,上网找找基本还是可以装好的,我这里是由于python版本比较多,依赖包装着装着就不知道装哪去了。
三、流程
代码如下,注释写的已经十分清晰了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import cv2 import dlib #定义一个鼠标事件,用户退出视频流循环 clicked = False def onMouse(event,x,y,flags,param): global clicked if event == cv2.EVENT_LBUTTONUP: clicked = True #打开摄像头,获取视频流 cameraCapture = cv2.VideoCapture(0) cv2.namedWindow('mywindow') cv2.setMouseCallback("mywindow",onMouse) success , frame = cameraCapture.read() #这里读取圣诞帽的图片,注意这里要加cv2.IMREAD_UNCHANGED参数,读取alpha通道的值。 hat_img = cv2.imread("/Users/longy/Desktop/hat.png",cv2.IMREAD_UNCHANGED) r,g,b,a = cv2.split(hat_img) rgb_hat = cv2.merge((r, g, b)) cv2.imwrite("hat_alpha.jpg", a) #这里原文章是用的5点,不过网上没找到,所以用的68点识别,不过都一样用。 predictor_path = "shape_predictor_68_face_landmarks.dat" predictor = dlib.shape_predictor(predictor_path) # dlib 正脸检测器 detector = dlib.get_frontal_face_detector() #print(frame) while success and cv2.waitKey(1) == -1 and not clicked: # 正脸检测 dets = detector(frame, 1) # 如果检测到人脸 if len(dets) > 0: for d in dets: x, y, w, h = d.left(), d.top(), d.right() - d.left(), d.bottom() - d.top() # x,y,w,h = faceRect cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2, 8, 0) # 关键点检测,68 个关键点 shape = predictor(frame, d) for point in shape.parts(): cv2.circle(frame, (point.x, point.y), 3, color=(0, 255, 0)) #两眼角的点用蓝色标识 cv2.circle(frame, (shape.part(36).x, shape.part(36).y), 3, color=(255,0, 0)) cv2.circle(frame, (shape.part(45).x, shape.part(45).y), 3, color=(255, 0, 0)) # 求两点中心 point1 = shape.part(36) point2 = shape.part(45) eyes_center = ((point1.x + point2.x) // 2, (point1.y + point2.y) // 2) # 根据人脸大小调整帽子大小,感觉这里原文章好像计算有点问题,我这里也没有改。 factor = 1.5 resized_hat_h = int(round(rgb_hat.shape[0] * w / rgb_hat.shape[1] * factor)) resized_hat_w = int(round(rgb_hat.shape[1] * w / rgb_hat.shape[1] * factor)) if resized_hat_h > y: resized_hat_h = y - 1 # 根据人脸大小调整帽子大小 resized_hat = cv2.resize(rgb_hat, (resized_hat_w, resized_hat_h)) # 用 alpha 通道作为 mask mask = cv2.resize(a,(resized_hat_w,resized_hat_h)) mask_inv = cv2.bitwise_not(mask) # 帽子相对与人脸框上线的偏移量 dh = 0 dw = 0 # 原图 ROI # bg_roi = img[y+dh-resized_hat_h:y+dh, x+dw:x+dw+resized_hat_w] bg_roi = frame[y + dh - resized_hat_h:y + dh,(eyes_center[0] - resized_hat_w // 3):(eyes_center[0] + resized_hat_w // 3 * 2)] # 原图 ROI 中提取放帽子的区域 bg_roi = bg_roi.astype(float) mask_inv = cv2.merge((mask_inv, mask_inv, mask_inv)) alpha = mask_inv.astype(float) / 255 # 相乘之前保证两者大小一致(可能会由于四舍五入原因不一致) alpha = cv2.resize(alpha, (bg_roi.shape[1], bg_roi.shape[0])) # print("alpha size: ",alpha.shape) # print("bg_roi size: ",bg_roi.shape) bg = cv2.multiply(alpha, bg_roi) bg = bg.astype('uint8') # 提取帽子区域 hat = cv2.bitwise_and(resized_hat,resized_hat,mask = mask) # 相加之前保证两者大小一致(可能会由于四舍五入原因不一致) hat = cv2.resize(hat, (bg_roi.shape[1], bg_roi.shape[0])) # 两个 ROI 区域相加 add_hat = cv2.add(bg, hat) # cv2.imshow("add_hat",add_hat) # 把添加好帽子的区域放回原图 frame[y + dh - resized_hat_h:y + dh,(eyes_center[0] - resized_hat_w // 3):(eyes_center[0] + resized_hat_w // 3 * 2)] = add_hat cv2.imshow("mywindow",frame) success,frame = cameraCapture.read() cv2.destroyWindow("mywindow") cameraCapture.release() |
哦 最后写下参考的知乎上的文章,知乎日报。