当前位置: 首页 > 科技观察

圣诞节,用Python给自己加一顶“圣诞帽”

时间:2023-03-14 14:16:38 科技观察

每年这个时候,微信好友的头像都会开始换成“圣诞”皮肤。最常见的是加一顶小圣诞帽。当然,这种事情很多P图软件都可以搞定,微信搜索“圣诞帽”就会出现各种小程序,但是使用之前的验证,总会被要求绑定各种信息,比如作为微信,接受各种公告。画面广告,有的甚至需要分享才能保存图片。那么作为程序员,有没有其他的方式来添加呢?当然!使用的工具:OpenCV(毕竟我们的主要内容还是OpenCV。。。)dlib(dlib的人脸检测比OpenCV好,dlib有OpenCV没有关键点检测。)使用的语言:Python,不过可以改成C++版本。素材准备首先我们要准备一个圣诞帽素材,格式最好是PNG,因为我们可以直接用Alpha通道作为PNG的蒙版。我们使用的圣诞帽如下:我们可以通过通道分离得到圣诞帽图像的Alpha通道。代码如下:r,g,b,a=cv2.split(hat_img)rgb_hat=cv2.merge((r,g,b))cv2.imwrite("hat_alpha.jpg",a)为了得到能够匹配rgb通道的头像图片的操作,我们将rgb的三个通道组合成一个rgb颜色的帽子图。Alpha通道的图像如下图所示:人脸检测和人脸关键点检测我们使用下图作为我们的测试图:下面我们使用dlib的frontfacedetector进行人脸检测,使用dlib提供的模型进行提取人脸的五个关键点。代码如下:#dlib人脸关键点检测器predictor_path="shape_predictor_5_face_landmarks.dat"predictor=dlib.shape_predictor(predictor_path)#dlib正面人脸检测器detector=dlib.get_frontal_face_detector()#正面人脸检测dets=detector(img,1)#如果检测到人脸iflen(dets)>0:fordindets:x,y,w,h=d.left(),d.top(),d.right()-d.left(),d.bottom()-d.top()#x,y,w,h=faceRectcv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2,8,0)#关键点检测,5个关键点shape=predictor(img,d)forpointinshape.parts():cv2.circle(img,(point.x,point.y),3,color=(0,255,0))cv2.imshow("image",img)cv2.waitKey()这部分的效果如下:为了调整帽子的大小,我们在眼角处选择两个点,找到中心点为帽子的x方向的参考坐标,y方向的坐标用人脸边框上的线条的y坐标来表示。然后我们根据人脸检测检测到的人脸大小来调整帽子的大小,让帽子的大小合适。#选择左右眼角的点point1=shape.part(0)point2=shape.part(2)#求两点的中心eyes_center=((point1.x+point2.x)//2,(point1.y+point2.y)//2)#cv2.circle(img,eyes_center,3,color=(0,255,0))#cv2.imshow("image",img)#cv2.waitKey()#根据脸的大小调整帽子大小factor=1.5resized_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))ifresized_hat_h>y:resized_hat_h=y-1#根据脸的大小调整帽子大小resized_hat=cv2.resize(rgb_hat,(resized_hat_w,resized_hat_h))提取帽子和需要添加帽子的区域按照之前描述的去Alpha通道作为掩码并取反。这两个遮罩其中一个用于去除帽子图中的帽子区域,另一个用于清空角色图中需要填充帽子的区域。后面会看到:#使用alpha通道asmaskmask=cv2.resize(a,(resized_hat_w,resized_hat_h))mask_inv=cv2.bitwise_not(mask)从原图中取出需要添加帽子的区域,这里我们使用按位运算。#帽子与人脸框上线的偏移量dh=0dw=0#原图ROI#bg_roi=img[y+dh-resized_hat_h:y+dh,x+dw:x+dw+resized_hat_w]bg_roi=img[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("alphasize:",alpha.shape)#print("bg_roisize:",bg_roi.shape)bg=cv2.multiply(alpha,bg_roi)bg=bg.astype('uint8')这个就是下图的背景区域(bg)。可以看到需要用帽子填充的区域缺失了。然后我们提取帽子区域,代码如下:#提取帽子区域hat=cv2.bitwise_and(resized_hat,resized_hat,mask=mask)提取的帽子区域如下图所示。帽子区域正好与之前的背景区域互补。添加圣诞帽***我们将这两个区域加在一起。放回原图,就可以得到我们想要的圣诞帽的图片了。这里需要注意的是,在相加之前,resize要保证两者大小相同,因为四舍五入可能会出现不一致的情况。#添加前确保两者大小相同(可能因四舍五入不一致)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)#把添加的帽子区域放回原图img[y+dh-resized_hat_h:y+dh,(eyes_center[0]-resized_hat_w//3):(eyes_center[0]+resized_hat_w//3*2)]=add_hat得到的效果图如下图:***大家圣诞快乐!