使用opencv和dlib进行人脸三角刨分
使用opencv和dlib进行人脸三角刨分
- 什么是Delaunay三角剖分?
- 什么是Voronoi图?
- python 代码
- C++ 代码
什么是Delaunay三角剖分?

图:Delaunay三角剖分偏爱小角度
在一个平面上的一组离散点集合中进行三角剖分意味着将该平面划分为若干个互不重叠且覆盖整个平面的三角形区域,并且这些区域的所有顶点都必须是原始数据中的离散点集合中的某一点。
在这种情况下图2展示了四个关键节点A、B、C和D及其对应的德劳尼分割结果。
Delaunay triangulation exhibits a notable characteristic by avoiding the use of triangles with significant angular disparity.
图2描绘了当移动点时三角剖分如何演变以捕获"胖"三角形。位于顶端图像中的点B与D具有相同的x坐标值为1.5,在底端图像中,则它们的x坐标值被调整为1.75。顶端视角下形成的角ABC与ABD相对较大;相比之下,在顶端视角下形成的角ABC与ABD相对较大(注:此处可能有表述重复,请根据具体上下文进行调整)。然而,在底端视角下形成的角BCD过大,则采用边AC将其分割成更小的部分
多种算法可用于生成一组点集的Delaunay triangulation. 其中最为经典但并非最优的方法是:从任意一个初始分割出发,并依次验证每个triangle的circumcircle是否包含其他顶点. 如果满足条件,则交换edge(如图所示),直至所有生成的triangle都不再被其circumcircle所包含.

什么是Voronoi图?
给定平面中的一组点,在Voronoi图中将空间划分为若干区域,并使各区域边界的线条与相邻点等距分布。图3展示了基于黑色显示的点计算所得Voronoi图的具体实例。观察到的是,在每条边界线上都会穿过两个相邻点之间的中位线位置。如果将相邻Voronoi区域内的所有点进行连接,则能够构造出对应的Delaunay三角剖分!
Delaunay三角剖分和Voronoi图之间的联系多种多样,并非单一关系。
乔治·沃罗诺伊(Georgy Voronoy)是鲍里斯·德劳内(Boris Delaunay)的博士生导师。
给定一组点,您可以使用Subdiv2D类计算Delaunay三角剖分或Voronoi图。

人脸动态三角刨分
python 代码
#!/usr/bin/python
import cv2
import numpy as np
import dlib
import random
# Check if a point is inside a rectangle
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
# Draw a point
def draw_point(img, p, color ) :
cv2.circle( img, p, 2, color, -1, 1, 0 )
# Draw delaunay triangles
def draw_delaunay(img, subdiv, delaunay_color ) :
triangleList = subdiv.getTriangleList();
size = img.shape
r = (0, 0, size[1], size[0])
for t in triangleList :
pt1 = (t[0], t[1])
pt2 = (t[2], t[3])
pt3 = (t[4], 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,1, 0)
cv2.line(img, pt2, pt3, delaunay_color, 1, 1, 0)
cv2.line(img, pt3, pt1, delaunay_color, 1, 1, 0)
cv2.imshow(win_delaunay,img)
cv2.waitKey(100)
# Draw voronoi diagram
def draw_voronoi(img, subdiv) :
( facets, centers) = subdiv.getVoronoiFacetList([])
for i in range(0,len(facets)) :
ifacet_arr = []
for f in facets[i] :
ifacet_arr.append(f)
ifacet = np.array(ifacet_arr, np.int)
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
cv2.fillConvexPoly(img, ifacet, color,1, 0);
ifacets = np.array([ifacet])
cv2.polylines(img, ifacets, True, (0, 0, 0), 1, 1, 0)
cv2.circle(img, (centers[i][0], centers[i][1]), 3, (0, 0, 0), -1, 1, 0)
if __name__ == '__main__':
# Define window names
win_delaunay = "Delaunay Triangulation"
win_voronoi = "Voronoi Diagram"
# Turn on animation while drawing triangles
animate = True
# Define colors for drawing.
delaunay_color = (255,255,255)
points_color = (0, 0, 255)
# Read in the image.
img = cv2.imread("1.jpg");
# Keep a copy around
img_orig = img.copy();
# Rectangle to be used with Subdiv2D
size = img.shape
rect = (0, 0, size[1], size[0])
# Create an instance of Subdiv2D
subdiv = cv2.Subdiv2D(rect);
# Create an array of points.
points = [];
# Read in the points from a text file
detector = dlib.get_frontal_face_detector() # 使用特征提取器frontal_face_detector
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # dlib的68点模型
dets = detector(img, 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()))
# Get the landmarks/parts for the face in box d.
shape = predictor(img, d) # 利用预测器预测
for i in range(68):
points.append((int(shape.part(i).x), int(shape.part(i).y)))
# Insert points into subdiv
for p in points :
subdiv.insert(p)
# Show animation
if animate :
img_copy = img_orig.copy()
# Draw delaunay triangles
draw_delaunay( img_copy, subdiv, (255, 255, 255) );
cv2.imshow(win_delaunay, img_copy)
cv2.waitKey(100)
# Draw delaunay triangles
draw_delaunay( img, subdiv, (255, 255, 255) );
# Draw points
for p in points :
draw_point(img, p, (0,0,255))
# Allocate space for Voronoi Diagram
img_voronoi = np.zeros(img.shape, dtype = img.dtype)
# Draw Voronoi diagram
draw_voronoi(img_voronoi,subdiv)
# Show results
cv2.imshow(win_voronoi,img_voronoi)
cv2.waitKey(0)
C++ 代码
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <dlib/opencv.h>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include <iostream>
#include <fstream>
using namespace cv;
using namespace dlib;
using namespace std;
#define RATIO 4
#define SKIP_FRAMES 2
// Draw a single point
static void draw_point(Mat& img, Point2f fp, Scalar color)
{
circle(img, fp, 2, color, CV_FILLED, CV_AA, 0);
}
// Draw delaunay triangles
static void draw_delaunay(Mat& img, Subdiv2D& subdiv, Scalar delaunay_color)
{
std::vector<Vec6f> triangleList;
subdiv.getTriangleList(triangleList);
std::vector<Point> pt(3);
Size size = img.size();
Rect rect(0, 0, size.width, size.height);
for (size_t i = 0; i < triangleList.size(); i++)
{
Vec6f t = triangleList[i];
pt[0] = Point(cvRound(t[0]), cvRound(t[1]));
pt[1] = Point(cvRound(t[2]), cvRound(t[3]));
pt[2] = Point(cvRound(t[4]), cvRound(t[5]));
// Draw rectangles completely inside the image.
if (rect.contains(pt[0]) && rect.contains(pt[1]) && rect.contains(pt[2]))
{
cv::line(img, pt[0], pt[1], delaunay_color, 1, CV_AA, 0);
cv::line(img, pt[1], pt[2], delaunay_color, 1, CV_AA, 0);
cv::line(img, pt[2], pt[0], delaunay_color, 1, CV_AA, 0);
}
}
}
//Draw voronoi diagram
static void draw_voronoi(Mat& img, Subdiv2D& subdiv)
{
std::vector<std::vector<Point2f> > facets;
std::vector<Point2f> centers;
subdiv.getVoronoiFacetList(std::vector<int>(), facets, centers);
std::vector<Point> ifacet;
std::vector<std::vector<Point> > ifacets(1);
for (size_t i = 0; i < facets.size(); i++)
{
ifacet.resize(facets[i].size());
for (size_t j = 0; j < facets[i].size(); j++)
ifacet[j] = facets[i][j];
Scalar color;
color[0] = std::rand() & 255;
color[1] = std::rand() & 255;
color[2] = std::rand() & 255;
fillConvexPoly(img, ifacet, color, 8, 0);
ifacets[0] = ifacet;
polylines(img, ifacets, true, Scalar(), 1, CV_AA, 0);
circle(img, centers[i], 3, Scalar(), CV_FILLED, CV_AA, 0);
}
}
int main(int argc, char** argv)
{
// Define window names
string win_delaunay = "Delaunay Triangulation";
string win_voronoi = "Voronoi Diagram";
// Turn on animation while drawing triangles
bool animate = true;
// Define colors for drawing.
Scalar delaunay_color(255, 255, 255), points_color(0, 0, 255);
// Read in the image.
Mat img = imread("2.jpg");
// Keep a copy around
Mat img_orig = img.clone();
// Rectangle to be used with Subdiv2D
Size size = img.size();
Rect rect(0, 0, size.width, size.height);
// Create an instance of Subdiv2D
Subdiv2D subdiv(rect);
// Create a vector of points.
std::vector<Point2f> points;
// Read in the points from a text file
int count = 0;
frontal_face_detector detector = get_frontal_face_detector();
shape_predictor pose_model;
deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;
std::vector<dlib::rectangle> faces;
Mat img_small;
resize(img, img_small, Size(), 1.0 / RATIO, 1.0 / RATIO);
cv_image<bgr_pixel> cimg(img);
cv_image<bgr_pixel> cimg_small(img_small);
// Detect faces
if (count++ % SKIP_FRAMES == 0) {
faces = detector(cimg_small);
}
// Find the pose of each face.
std::vector<full_object_detection> shapes;
for (unsigned long i = 0; i < faces.size(); ++i) {
dlib::rectangle r(
(long)(faces[i].left() * RATIO),
(long)(faces[i].top() * RATIO),
(long)(faces[i].right() * RATIO),
(long)(faces[i].bottom() * RATIO)
);
// Landmark detection on full sized image
full_object_detection shape = pose_model(cimg, r);
shapes.push_back(shape);
for (i=0;i<68;i++)
{
points.push_back(Point2f(shape.part(i).x(), shape.part(i).y()));
}
}
// Insert points into subdiv
for (std::vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
{
subdiv.insert(*it);
// Show animation
if (animate)
{
Mat img_copy = img_orig.clone();
// Draw delaunay triangles
draw_delaunay(img_copy, subdiv, delaunay_color);
imshow(win_delaunay, img_copy);
waitKey(100);
}
}
// Draw delaunay triangles
draw_delaunay(img, subdiv, delaunay_color);
// Draw points
for (std::vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
{
draw_point(img, *it, points_color);
}
// Allocate space for Voronoi Diagram
Mat img_voronoi = Mat::zeros(img.rows, img.cols, CV_8UC3);
// Draw Voronoi diagram
draw_voronoi(img_voronoi, subdiv);
// Show results.
imshow(win_delaunay, img);
imshow(win_voronoi, img_voronoi);
waitKey(0);
return 0;
}
