先获取人脸68个特征点坐标,其中使用了官方的预训练模型shape_predictor_68_face_landmarks.dat:
- import dlib
- import cv2
-
- predictor_path = "shape_predictor_68_face_landmarks.dat"
- png_path = "face.jpg"
-
- txt_path = "points.txt"
- f = open(txt_path, 'w+')
-
- # 与人脸检测相同,使用dlib自带的frontal_face_detector作为人脸检测器
- detector = dlib.get_frontal_face_detector()
- # 相撞
- # 使用官方提供的模型构建特征提取器
- predicator = dlib.shape_predictor(predictor_path)
- win = dlib.image_window()
- img1 = cv2.imread(png_path)
-
- dets = detector(img1, 1)
- print("Number of faces detected : {}".format(len(dets)))
- for k, d in enumerate(dets):
- print("Detection {} left:{} Top: {} Right {} Bottom {}".format(
- k, d.left(), d.top(), d.right(), d.bottom()
- ))
- lanmarks = [[p.x, p.y] for p in predicator(img1, d).parts()]
- for idx, point in enumerate(lanmarks):
- f.write(str(point[0]))
- f.write("\t")
- f.write(str(point[1]))
- f.write('\n')
实现人脸三角剖分:
- # 日期: 2023/11/2 23:04
- import cv2
- import numpy as np
- import random
-
-
- # 检查点是否在矩形框内
- def rect_contains(rect, point):
- if point[0] < rect[0]:
- return False
- elif point[1] < rect[1]:
- return False
- elif point[0] > rect[2]:
- return False
- elif point[1] > rect[3]:
- return False
- return True
-
-
- # 画点
- def draw_point(img, p, color):
- cv2.circle(img, p, 2, color)
-
-
- # 绘制德劳内三角形
- def draw_delaunay(img, subdiv, delaunay_color):
- trangleList = subdiv.getTriangleList() # 获取Delaunay三角形的列表
- size = img.shape
- r = (0, 0, size[1], size[0])
- for t in trangleList:
- pt1 = (int(t[0]), int(t[1]))
- pt2 = (int(t[2]), int(t[3]))
- pt3 = (int(t[4]), int(t[5]))
- if rect_contains(r, pt1) and rect_contains(r, pt2) and rect_contains(r, pt3):
- cv2.line(img, pt1, pt2, delaunay_color, 1) # 源图像,线段的两个端点,颜色,线宽
- cv2.line(img, pt2, pt3, delaunay_color, 1)
- cv2.line(img, pt3, pt1, delaunay_color, 1)
-
-
- # Draw voronoi diagram
- def draw_voronoi(img: object, subdiv: object) -> object:
- (facets, centers) = subdiv.getVoronoiFacetList([]) # 获取Voronoi构面的列表
-
- # 对于每个voronoi多边形
- for i in range(0, len(facets)):
- ifacet_arr = []
- # 得到每个多边形的顶点
- for f in facets[i]:
- ifacet_arr.append(f)
-
- ifacet = np.array(ifacet_arr, dtype=np.int32)
- # 随机颜色
- color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- # 填充颜色
- cv2.fillConvexPoly(img, ifacet, color) # 图像、多边形顶点、颜色
- vertex = np.array([ifacet])
- cv2.polylines(img, vertex, True, (0, 0, 0), 1) # 绘制多边形,参数包括图像、多边形的点、线条是否闭合、颜色和线条宽度
- cv2.circle(img, (centers[i][0], centers[i][1]), 3, (0, 0, 0)) # 绘制圆,参数包括图像、中心点、半径、颜色
-
-
- if __name__ == '__main__':
- # 定义窗口名称
- win_delaunary = "Delaunay Triangulation"
- win_voronoi = "Voronoi Diagram"
-
- # 在画三角形的时候开启动画
- animate = True
-
- # 定义画的颜色
- delaunary_color = (255, 255, 255)
- points_color = (0, 0, 255)
-
- # 读入图片
- img_path = "face.jpg"
-
- img = cv2.imread(img_path)
-
- # 复制
- img_orig = img.copy()
-
- # 矩形框用于Subdiv2D
- size = img.shape # h, w, channel
- # x,y,w,h
- rect = (0, 0, size[1], size[0])
-
- # 创建一个Subdiv2D的实例
- subdiv = cv2.Subdiv2D(rect)
-
- # 创建点的列表
- points = []
- # 从文档中读取点的坐标
- with open("points.txt") as file:
- for line in file:
- x, y = line.split()
- points.append((int(x), int(y)))
- # 向subdiv中插入点
- for p in points:
- subdiv.insert(p)
- # 展示动画效果
- if animate:
- img_copy = img_orig.copy()
- # 绘制德劳内三角形
- draw_delaunay(img_copy, subdiv, (255, 255, 255))
- cv2.imshow(win_delaunary, img_copy)
- cv2.waitKey(100)
-
- # 绘制德劳内三角形
- draw_delaunay(img, subdiv, (255, 255, 255))
-
- # 绘制点
- for p in points:
- draw_point(img, p, (0, 0, 255))
-
- # 为沃罗诺伊图分配空间
- img_voronoi = np.zeros(img.shape, dtype=img.dtype)
-
- # 绘制沃罗诺伊图
- draw_voronoi(img_voronoi, subdiv)
-
- # 展示结果
- cv2.imshow(win_delaunary, img)
- cv2.imshow(win_voronoi, img_voronoi)
- cv2.waitKey(0)