在线购物商城网站,江苏营销型网站,代理记账公司收费标准,中国大基建最新消息三角剖分概念 三角剖分#xff08;Triangulation#xff09;是一种将多边形或曲面分解为一系列互不相交的三角形的技术#xff0c;它是计算几何、计算机图形学、地理信息系统、工程和科学计算中的一个基本概念。通过三角剖分#xff0c;复杂的形状可以被简化为基本的三角… 三角剖分概念 三角剖分Triangulation是一种将多边形或曲面分解为一系列互不相交的三角形的技术它是计算几何、计算机图形学、地理信息系统、工程和科学计算中的一个基本概念。通过三角剖分复杂的形状可以被简化为基本的三角形元素这些元素更容易处理和分析。
在二维空间中一个简单的三角剖分将一个多边形划分为若干个不相交的三角形这些三角形的边要么是多边形的边要么是连接多边形内部点的边。在三维空间中三角剖分通常应用于表面建模将一个曲面分割成一系列互不相交的三角面片。
三角剖分有多种类型其中最著名的一种是Delaunay三角剖分。在Delaunay剖分中一个点的邻接三角形的内切圆包含该点但不包含其他点的最小圆是唯一的且该点位于圆的外部。这种剖分在很多应用中非常有用因为它保证了相邻三角形之间的良好分布从而避免了过于狭长的三角形这对于数值计算和图形渲染来说是理想的。
三角剖分在以下领域有广泛应用
计算机图形学在3D建模和渲染中用于将复杂的模型简化为三角面片便于计算和显示。地理信息系统GIS用于将地图数据分割成三角形网格便于空间分析和数据可视化。有限元分析FEM在工程计算中用于将连续区域划分为离散的三角形单元以便于数值求解偏微分方程。游戏开发在游戏引擎中用于减少模型的多边形数量提高渲染效率。机器学习在某些算法中比如最近邻搜索Delaunay三角剖分可以作为数据结构加速计算。 三角剖分通常可以通过算法来实现例如Graham扫描、 Jarvis March 或者更高效的算法如incremental方法和flip-based方法。在实际应用中有许多现成的库和工具提供了三角剖分的功能例如CGALComputational Geometry Algorithms Library和Triangle库。 人脸对齐实现
实现步骤
1、检测人脸区域
2、对人脸区域进行关键点标识
3、考虑到人脸可以存在倾斜为了能更好的对齐效果可以对关键点做旋转矩阵拟合
4、这里以经典的人脸68关键点为示例为了边界部分更自然可以对旋转矩阵进行扩展
5、将每一个三角形与目标三角形进行仿射变换得到一张与目标点对齐的人脸 代码实现
扩展旋转矩阵区域
void increaseRect(std::vectorcv::Point2f points,const cv::Mat img)
{// 计算点集合构成的矩形的边界框cv::Rect rect cv::boundingRect(points);// 定义增加比例float scale 0.2;// 计算增加的宽度和高度int width rect.width*scale;int height rect.height*scale;// 定义增加值的lambda函数用于安全地增加或减少一个值auto addVal [](float val, float add, float max){val std::min(val add, max); // 确保值不会超过最大值};auto subVal [](float val, float sub, float min){val std::max(val - sub, min); // 确保值不会低于最小值};// 应用增加的宽度和高度来调整四个顶点的位置// 调整第一个点的x坐标和y坐标subVal(points[0].x, width, 0);addVal(points[0].y, height, img.rows);// 调整第二个点的x坐标和y坐标subVal(points[1].x, width, 0);subVal(points[1].y, height, 0);// 调整第三个点的x坐标和y坐标addVal(points[2].x, width, img.cols);subVal(points[2].y, height, 0);// 调整第四个点的x坐标和y坐标addVal(points[3].x, width, img.cols);addVal(points[3].y, height, img.rows);
}
增加边界点
void FillPointRect(const std::vectorcv::Point2f points, std::vectorcv::Point vecPoint)
{// 临时存储扩充的点std::vectorcv::Point addPointsTmp; // 根据原始点集通过向每个点添加偏移量来生成新的点addPointsTmp.push_back(points[0] cv::Point2f(1, -1));addPointsTmp.push_back(points[1] cv::Point2f(1, 1));addPointsTmp.push_back(points[2] cv::Point2f(-1, 1));addPointsTmp.push_back(points[3] cv::Point2f(-1, -1));// 通过现有点计算并加入新的点以构成矩形的外接多边形addPointsTmp.push_back((addPointsTmp[0] addPointsTmp[1])/2);addPointsTmp.push_back((addPointsTmp[1] addPointsTmp[2])/2);addPointsTmp.push_back((addPointsTmp[2] addPointsTmp[3])/2);addPointsTmp.push_back((addPointsTmp[3] addPointsTmp[0])/2);// 将扩充的点集添加到输出向量中vecPoint.insert(vecPoint.end(), addPointsTmp.begin(), addPointsTmp.end());
}
获取所有的三角形下标
//获取三角形下标
std::vectorcv::Vec3i Triangulation::getTrianglesIndexs(std::vectorcv::Vec6f triangleList, std::vectorcv::Point vecPoint)
{auto findPoint [vecPoint](cv::Point p){for (int i 0; i vecPoint.size(); i){if (vecPoint[i] p){return i;} }return -1;};std::vectorcv::Vec3i trianglesIndexs;for (auto item : triangleList){cv::Point p1 cv::Point(item[0], item[1]);cv::Point p2 cv::Point(item[2], item[3]);cv::Point p3 cv::Point(item[4], item[5]);auto idx1 findPoint(p1);auto idx2 findPoint(p2);auto idx3 findPoint(p3);if (idx1 ! -1 idx2 ! -1 idx3 ! -1){trianglesIndexs.push_back(cv::Vec3i(idx1, idx2, idx3));}}return trianglesIndexs;
}
绘制三角剖分图
std::vectorcv::Point vecPoint item.boxPoint;auto rotateRect cv::minAreaRect(vecPoint);cv::Point2f pot[4];rotateRect.points(pot);std::vectorcv::Point2f rotatePoints{pot[0], pot[1], pot[2], pot[3]};increaseRect(rotatePoints, img);FillPointRect(rotatePoints, vecPoint);std::vectorcv::Point vecHull;cv::convexHull(vecPoint, vecHull);auto rect cv::boundingRect(vecHull);cv::Subdiv2D subdiv(rect);std::vectorcv::Point2f vecPoint2f;for (auto ptIt : vecPoint){vecPoint2f.emplace_back(cv::Point2f(ptIt.x, ptIt.y));}subdiv.insert(vecPoint2f);std::vectorcv::Vec6f triangleList;subdiv.getTriangleList(triangleList);auto trianglesIndexs std::move(getTrianglesIndexs(triangleList, vecPoint));for (auto item : trianglesIndexs){cv::Point pt1 vecPoint[item[0]];cv::Point pt2 vecPoint[item[1]];cv::Point pt3 vecPoint[item[2]];cv::line(destDataOut-img(), pt1, pt2, 255);cv::line(destDataOut-img(), pt2, pt3, 255);cv::line(destDataOut-img(), pt3, pt1, 255);}
人脸对齐
cv::Mat alignmentImageData(const cv::Mat srcImg, const cv::Rect srcBox, std::vectorcv::Point vecPointRoi, std::vectorcv::Point vecDestPointRoi)
{auto srcImgRoi srcImg(srcBox);//构建三角剖分获取三角形下标cv::Subdiv2D subdiv(cv::Rect(0, 0, srcBox.width, srcBox.height));std::vectorcv::Point2f vecPoint2f;vecPoint2f.insert(vecPoint2f.end(), vecPointRoi.begin(), vecPointRoi.end());subdiv.insert(vecPoint2f);std::vectorcv::Vec6f triangleList;subdiv.getTriangleList(triangleList);auto trianglesIndexs std::move(getTrianglesIndexs(triangleList, vecPointRoi));qDebug() triangles size: trianglesIndexs.size();cv::Mat destNewImg srcImgRoi.clone();auto maxRoiSize srcImgRoi.size();for (auto item : trianglesIndexs){cv::Point tr1Pt1 vecPointRoi[item[0]];cv::Point tr1Pt2 vecPointRoi[item[1]];cv::Point tr1Pt3 vecPointRoi[item[2]];auto rect1 cv::boundingRect(std::vectorcv::Point{tr1Pt1, tr1Pt2, tr1Pt3});auto croppedTriangle srcImgRoi(rect1);cv::Point rect1Beg cv::Point(rect1.x, rect1.y);cv::Point tr2Pt1 vecDestPointRoi[item[0]];cv::Point tr2Pt2 vecDestPointRoi[item[1]];cv::Point tr2Pt3 vecDestPointRoi[item[2]];auto rect2 cv::boundingRect(std::vectorcv::Point{tr2Pt1, tr2Pt2, tr2Pt3});cv::Mat croppedTr2Mask cv::Mat::zeros(rect2.size(), CV_8UC1);//生成模板图像三角形掩码cv::Point rect2Beg cv::Point(rect2.x, rect2.y);std::vectorcv::Point maskPoints2 {tr2Pt1 - rect2Beg, tr2Pt2 - rect2Beg, tr2Pt3 - rect2Beg};cv::fillConvexPoly(croppedTr2Mask, maskPoints2, 255);cv::Point2f srcTri[3] {tr1Pt1 - rect1Beg, tr1Pt2 - rect1Beg, tr1Pt3 - rect1Beg};cv::Point2f dstTri[3] {tr2Pt1 - rect2Beg, tr2Pt2 - rect2Beg, tr2Pt3 - rect2Beg};auto warpMat cv::getAffineTransform(srcTri, dstTri);cv::Mat warpTriangle;cv::warpAffine(croppedTriangle, warpTriangle, warpMat, rect2.size());cv::bitwise_and(warpTriangle, warpTriangle, warpTriangle, croppedTr2Mask);cv::Mat roi destNewImg(rect2);warpTriangle.copyTo(roi, croppedTr2Mask);}return destNewImg;
}
人脸68关键点和5关键点三角剖分图 人脸对齐