AR学习笔记(三):配准特征点的选择

  • 特征点选择思路
  • 用到的库
    • OPENCV
    • DLIB
  • 特征点选择测试
    • 根据唇部轮廓线选择特征点
      • DLIB唇部识别测试
    • 根据牙齿的角点选择特征点
      • 边缘提取测试
      • 颜色分割测试
    • 轮廓检测提取特征点
    • 根据牙齿的质心选择特征点
    • 基于深度学习的方法
  • 问题

课题需要选择合适的特征点,计算相机的位姿变换矩阵,实现2D-3D配准的功能,编程语言主要采用C++,部分通过python验证,下面记录一下自己的思路


特征点选择思路

因为对3D模型特征点检测不熟悉,我主要还是在2D图像上选可自动检测的特征点,然后在3D模型上对应地选取

  1. 用dlib库识别唇部轮廓线,选择唇部内侧特征点,再确定牙模上的对应点
  2. 用opencv库进行图像处理,先分割出牙齿区域,再提取牙齿的边缘轮廓,最后提取牙齿的特征点
    我了解的特征点提取方法包括SIFT、SURF、ORB等,但常用在多帧2D图像的匹配跟踪上,在本项目里需要提取便于在牙模上直接确定的点,所以不适合
    我的思路是在提取到牙齿区域和轮廓的基础上,选择固定几颗牙齿的角点或者质心作为特征点,这样在牙模上的特征点也可以固定下来,角点提取的方法有harris角点、Shi-Tomasi角点等,质心计算则是提取canny边缘后找出每颗牙齿的轮廓,进而计算矩得到质心,或是提取最小外接矩形,取矩形中心点

用到的库

OPENCV

我用的opencv库是当前最新版的4.5.3,过程在上一篇博客

DLIB

官方文档:http://dlib.net/

Dlib is a modern C++ toolkit containing machine learning algorithms and tools for creating complex software in C++ to solve real world problems. It is used in both industry and academia in a wide range of domains including robotics, embedded devices, mobile phones, and large high performance computing environments.

Dlib是一个现代化的C ++工具箱,其中包含用于在C ++中创建复杂软件以解决实际问题的机器学习算法和工具,这里用来实现唇部的识别,版本是19.22

安装步骤:https://www.dazhuanlan.com/cbwintering/topics/1223874

$ mkdir build
$ cd build
$ cmake .. -DDLIB_USE_CUDA=0
$ cmake --build . --config Release
$ sudo make install

在github上找了几个dlib库人脸检测的项目

  1. Face_Recognition_dlib (python)
  2. Dlib_face_recognition_from_camera (python)
  3. head-pose-estimation (c++ python)

dilb对于侧脸的检测效果不好,这里有一个DAN方法(人脸对齐)项目:DeepAlignmentNetwork


特征点选择测试

这几天测试流程的安排:(粗体为已测试)

  1. 测试dlib库,检测到唇部轮廓点并显示,制作为视频
  2. 利用opencv库以及边缘检测的方法,提取牙齿的轮廓,检测角点
  3. 利用opencv库以及颜色分割的方法,分割牙齿的图像,检测角点
  4. 利用opencv库以及矩形轮廓检测的方法,框出牙齿的位置,进而获取特征点
  5. 在4的基础上计算牙齿轮廓的矩信息,实现质心的提取(牙齿质心应该在一条抛物线上)
  6. 深度学习的方法

根据唇部轮廓线选择特征点

先通过dlib库识别唇部的特征点,包含内侧唇部轮廓和外侧唇部轮廓,以及嘴角点

通过IvoSmile APP猜了一下它用的方法,主要有两个功能:

  1. 美白,我认为它使用dlib先识别了唇部轮廓线,定位到了唇部位置,然后识别了牙齿(可能是颜色分割)进行美白,我测试了它的实时美白功能,在偏向一侧角度稍大时(大概45度)就会识别不出来,这种情况在我测试dlib人脸识别时也出现过;
  2. 牙齿修复,首先应该还是用dlib识别唇部轮廓线,特征点应该是内部轮廓线对称的几个点,默认的牙模特征点应该是牙齿顶部的点,进行了配准。在测试过程中,首先是需要拍一张正面的照片,用于确定中心点便于特征点的统一,然后拍摄一段图片序列,自动将3D牙模投影到了2D图像上。
    PS:我在拍摄的过程中只露出一半牙齿,而牙模投影时是整颗牙齿,如下图所示,左侧为原始牙齿,右侧为修复投影的牙齿(说明2D图像特征点选取不在牙齿的下端)
    后续还要进行手动的调整,才准确完成了2D-3D配准,此外,在直播模式中,同样侧过45度左右就会失效。

如果要直接完成配准不进行手动调节,那么特征点必须选取在牙齿上,如果仍选择唇部特征点,那么对于照片的拍摄会有更高的要求(尽量露出整颗牙齿,即唇部内轮廓线刚好在牙齿的顶部)


DLIB唇部识别测试

参考python实现:https://blog.csdn.net/AAliuxiaolei/article/details/107821085
在DLIB中面部识别了68个点,取49-68号点即可取出唇部特征点,建立工程dlib_test,程序为mouth_detect.py和video_dlib.py,结果放在了results文件夹中,整体效果比较好,在侧脸角度比较大的时候还是会出现检测不到人脸的情况,计算速度有点慢

下面是导出视频的一张截图


后面写了一份c++版本的代码配合opencv,用来提取唇部的图像,便于后续的牙齿分割,下面是部分代码

#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 <dlib/image_io.h>
#include <dlib/opencv.h>#include <iostream>
#include <vector>
#include <ctime>using namespace std;
using namespace cv;void get_mouth_points(Mat img, std::vector<dlib::full_object_detection> fs) //提取唇部特征点
{int i, j;for(j=0; j<fs.size(); j++){Point p1, p2;for(i = 48; i<59; i++){//嘴唇外圈  48 ~ 59//嘴唇内圈  59 ~ 67switch(i){case 16:case 21:case 26:case 30:case 35:case 41:case 47:case 59:i++;break;default:break;}p1.x = fs[j].part(i).x();p1.y = fs[j].part(i).y();p2.x = fs[j].part(i+1).x();p2.y = fs[j].part(i+1).y();//cv::line(img, p1, p2, cv::Scalar(0,0,255), 2, 4, 0);if(i == 58)mask_points.push_back(p2);elsemask_points.push_back(p1);}}
}int main()
{Mat frame = cv::imread("front.jpg");Mat dst;// 减小图像尺寸 1200*800Mat img_resize = Mat::zeros(1200,800 , CV_8UC3); resize(frame, frame, img_resize.size());//提取灰度图cvtColor(frame, dst, CV_BGR2GRAY);//加载dlib的人脸识别器dlib::frontal_face_detector detector = dlib::get_frontal_face_detector();//加载人脸形状探测器dlib::shape_predictor sp;dlib::deserialize("./shape_predictor_68_face_landmarks.dat") >> sp;//Mat转化为dlib的matrixdlib::array2d<dlib::bgr_pixel> dimg;dlib::assign_image(dimg, dlib::cv_image<uchar>(dst)); //获取一系列人脸所在区域std::vector<dlib::rectangle> dets = detector(dimg);std::cout << "Number of faces detected: " << dets.size() << std::endl;if (dets.size() == 0)return 0;//获取人脸特征点分布std::vector<dlib::full_object_detection> shapes;int i = 0;for(i = 0; i < dets.size(); i++){dlib::full_object_detection shape = sp(dimg, dets[i]); //获取指定一个区域的人脸形状shapes.push_back(shape); }   //获取唇部特征点集get_mouth_points(frame, shapes);
}

得到的结果如下,自动分割出唇部区域图像


根据牙齿的角点选择特征点

只利用opencv来进行图像分割的话,会有很多噪声干扰,我觉得先定位到嘴的区域比较方便处理,然后采用边缘提取或者颜色分割的方法,将牙齿分割出来,然后计算角点作为特征点,想象中角点应该在牙齿的端点(需要测试),对应的牙模取点也可以选取在牙齿(类似矩形)的端点。

边缘提取测试

思路:假设已经提取到了嘴部的区域(通过上面的方法可以做到),设置了ROI,然后进行gamma矫正和高斯滤波,平滑掉细小噪声(尤其是牙齿的高光),通过形态学处理中的开运算分开每颗牙齿,最后进行canny算子提取边缘
结果:原图/开运算处理/Canny边缘结果如下图所示,牙齿能够分离出来,对于侧脸效果应该也类似,但是还是存在不想要的边缘,阈值设置起来也比较麻烦

能否先对第一帧提取特征点,然后通过SIFT/SURF这些方法进行后续图像的特征点匹配来跟踪?


测试代码(部分):

#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/imgproc.hpp>
#include "opencv2/imgproc/imgproc_c.h"
#include <iostream>using namespace cv;
using namespace std;int main()
{Mat img, img_gray, img_blur, img_canny, bw;int edgeThresh = 1;int lowThreshold = 20;int ratio = 2;int kernel_size = 3;img = imread("front.jpg", 1);// 减小图像尺寸 1200*800Mat img_resize = Mat::zeros(1200,800 , CV_8UC3); resize(img, img_resize, img_resize.size());// 设置ROI取出嘴部图像Rect myROI(320, 680, 200, 100);Mat mask = Mat::zeros(img_resize.size(), CV_8UC1);mask(myROI).setTo(255);Mat img_ROI;img_resize.copyTo(img_ROI, mask);imshow("img_ROI", img_ROI);waitKey(0);// 图像预处理cvtColor(img_ROI,img_gray,CV_BGR2GRAY);//直方图均衡化/*equalizeHist(img_gray, img_gray);imshow("img_equalhist", img_gray);waitKey(0);*/// gamma矫正MyGammaCorrection(img_gray,  img_gray, 2);// 高斯平滑GaussianBlur(img_gray, img_blur,  Size(5,5), 0, 0);// 腐蚀膨胀去除牙齿的细小纹路bw=(img_blur>75);Mat   Structure0=getStructuringElement(MORPH_RECT,Size(5,5));erode(bw,bw,Structure0,Point(-1,-1));Mat Structure1=getStructuringElement(MORPH_RECT,Size(3,3));dilate(bw,bw,Structure1, Point(-1,-1));imshow("img_ed", bw);waitKey(0);// canny边缘提取Canny( bw, img_canny, lowThreshold, lowThreshold*ratio, kernel_size );imshow("img_canny", img_canny);waitKey(0);return 0;
}

后面测试了一下harris角点检测,参数没太细调,效果很差,牙齿本身不太规则,角点检测出来很难具有代表性,感觉用角点检测不太稳定,结果如下图

在用dilb提取唇部区域后再进行上述操作,得到结果如下


颜色分割测试

参考【opencv学习之二十九】彩色分割 写了一个HSV颜色分割的程序
同样先把嘴部区域取出来,单独进行颜色分割,调整了一下阈值,把牙齿的部分提取出来,结果如下


做了一下canny边缘提取和harris角点检测,结果如下

相对直接进行边缘提取,把牙齿外的图像滤除掉了,但是角点提取的效果还是不好,不够稳定


轮廓检测提取特征点

直接计算harris角点的方法不太好,打算先把每颗牙齿的外接最小矩形找出来,然后取矩形中心点作为特征点,这里用到的函数是findContours()和minAreaRect(),结果画在原图上,部分代码如下

  Mat image = bw.clone();vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());for (int i = 0; i < contours.size(); i++){/*//获取区域的面积,如果小于某个值就忽略double area_1 = contourArea(contours[i]);cout << area_1 << endl;if (area_1 < 40) continue;*///绘制轮廓的最小外接矩形RotatedRect rect = minAreaRect(contours[i]);//绘制最小外接矩形的中心点circle(img_ROI, Point(rect.center.x, rect.center.y), 2, Scalar(0, 255, 0), -1, 8);  //绘制最小外接矩形Point2f P[4];rect.points(P);vector<int> X_Contours;for (int j = 0; j <= 3; j++){X_Contours.push_back(P[j].x);X_Contours.push_back(P[(j + 1) % 4].x);line(img_ROI, P[j], P[(j + 1) % 4], Scalar(255, 255, 255), 2);}}imshow("contours", img_ROI);waitKey(0);

提取结果如下图,中心点基本都在牙齿的中心位置,受形态学处理影响比较大,结果可能会有点偏差,另外在提取边缘时必须把每颗牙齿分开,否则不能正确提取特征点

取第二张图片12clock进行测试结果如下

dlib库提取唇部区域后再进行上述操作得到结果如下


根据牙齿的质心选择特征点

记得上课的时候提到过图像的矩,这种描述子具有一定的尺度、旋转不变性,所以测试了一下,根据canny边缘提取得到的图像,用findContours()得到每颗牙齿的轮廓,然后调用moments计算图像的质心,参考了opencv11-计算不规则图像的质心,部分代码如下:

  vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());//计算轮廓矩vector<Moments> mu(contours.size() );       for( int i = 0; i < contours.size(); i++ )     {   mu[i] = moments( contours[i], false );   }     //计算轮廓的质心vector<Point2f> mc( contours.size() );      for( int i = 0; i < contours.size(); i++ )     {   mc[i] = Point2d( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );   }     for (int i = 0; i < contours.size(); i++){//绘制质心circle( img_ROI, mc[i], 2, Scalar( 255, 0, 255), -1, 8, 0 );  //char tam[100];//sprintf(tam, "(%0.0f,%0.0f)",mc[i].x,mc[i].y);//putText(img_ROI, tam, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.4, cvScalar(255,0,255),1);}imshow("contours", img_ROI);waitKey(0);

得到的结果如下


取第二张图片12clock测试结果如下,有偏差应该是与光照不均和阈值设置有关,上嘴唇的反光太亮了,和牙齿几乎是一个颜色


dlib库提取唇部区域后再进行上述操作得到结果如下

ps.还有一个看起来比较有效的方法,但是博客没有给出具体的程序,所以还没有尝试

参考博客:https://www.jishufanyi.cn/222005.html 把牙齿类似成圆形来检测

1.对图像进行二值化处理,可以使用sklearn.preprocessing.binarize
2.计算所有非零像素的质心。 这是图像中的中心蓝色圆圈。 称这个为structure_centroid
3.以structure_centroid的位置为中心,制作整个图像的极片,参见: polarTransform库
4.为这些极片中的每​​一个确定非零像素的质心的位置。找到点和曲线python之间的距离/找到一点到曲线的最小距离
5.包含这些质心的数组可提供牙齿平均位置的轨迹(路径)centroid_path
6.在能够检测到的,最接近centroid_path的圆上运行消除/选择算法,使用阈值距离降低异常值。


基于深度学习的方法

找了几个github上类似的项目,大部分都是检测牙齿,而不是提取特征点

  1. TeethDectcotor:基于TensorFlow检测牙齿 (python)
  2. TeethCV:Incisor Segmentation project. The implementation is using Active Shape Model approach to identify teeth in radiograph image (python)
  3. ToothFeaturePoints:An algorithm to get feature points from a mesh model (python)
  4. ToothDetectionAndColorMatch:色卡检测 (c++)

问题

  1. 目前的测试都是基于dlib检测出嘴部区域,再提取特征点,但是dlib的侧脸识别效果不好,比如素材中90度侧脸图片不能识别出来
    其他的人脸识别方法?
  2. 怎样把2D牙齿图像的特征点自动与3D模型对应起来(就是怎么判断提取的特征点对应的牙模位置)
  3. 怎么自动且准确地把每颗牙齿分割出来(在颜色分割中,阈值如何进行自动的调整)

AR学习笔记(三):配准特征点的选择相关推荐

  1. J2EE学习笔记三:EJB基础概念和知识 收藏

    J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...

  2. Caffe学习笔记4图像特征进行可视化

    Caffe学习笔记4图像特征进行可视化 本文为原创作品,未经本人同意,禁止转载,禁止用于商业用途!本人对博客使用拥有最终解释权 欢迎关注我的博客:http://blog.csdn.net/hit201 ...

  3. 吴恩达《机器学习》学习笔记三——多变量线性回归

    吴恩达<机器学习>学习笔记三--多变量线性回归 一. 多元线性回归问题介绍 1.一些定义 2.假设函数 二. 多元梯度下降法 1. 梯度下降法实用技巧:特征缩放 2. 梯度下降法的学习率 ...

  4. AR学习笔记(七):阈值二值化优化与颜色分割的优化

    AR学习笔记(七):阈值二值化优化与颜色分割的优化 阈值二值化的优化 当前方案 图像预处理 阈值二值化 优化方案 otsu法 顶帽变换 分块阈值法 颜色分割的优化 当前方案 优化方案 HSV模型分割 ...

  5. Polyworks脚本开发学习笔记(三)-TREEVIEW进阶操作

    Polyworks脚本开发学习笔记(三)-TREEVIEW进阶操作 移动/交换对象的顺序 移动对象的顺序 TREEVIEW FEATURE MOVE ( 1,2 ) 将索引号为1和2的特征交换位置 T ...

  6. 投资信托学习笔记(三)

    投资信托学习笔记(三) 投资信托学习笔记(三) - 补充笔记 百度百科信托投资 https://baike.baidu.com/item/%E4%BF%A1%E6%89%98%E6%8A%95%E8% ...

  7. tensorflow学习笔记(三十二):conv2d_transpose (解卷积)

    tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...

  8. Ethernet/IP 学习笔记三

    Ethernet/IP 学习笔记三 原文为硕士论文: 工业以太网Ethernet/IP扫描器的研发 知网网址: http://kns.cnki.net/KCMS/detail/detail.aspx? ...

  9. iView学习笔记(三):表格搜索,过滤及隐藏列操作

    iView学习笔记(三):表格搜索,过滤及隐藏某列操作 1.后端准备工作 环境说明 python版本:3.6.6 Django版本:1.11.8 数据库:MariaDB 5.5.60 新建Django ...

最新文章

  1. dba用什么工具连接mysql_DBA必备的15款MySQL管理工具
  2. 在windows平台使用Apache James搭建邮件服务器以及使用C#向外网发送邮件
  3. HttpServletRequest看这篇文章就够了
  4. 《深入剖析Tomcat》一2.2 应用程序 1
  5. 【赏析】15个非常棒的使用CSS3的设计组合
  6. mysql数据库远程安装,Linux_15:解决远程安装数据库
  7. Xilinx Petalinux安装和使用
  8. linux远程桌面太卡,确保远程桌面管理顺畅稳定的方法
  9. 一种video视频兼容IE的模式
  10. Python爬虫系列之poizon爬虫newSign、sign、data加解密算法
  11. Vue项目 chrome页面崩溃:喔唷 崩溃了, 并出现警告Forced reflow while executing JavaScript took Nms
  12. 计算机怎么发音乐,网易云音乐怎么分享音乐给别人的教程
  13. 汽车数据流分析常采用哪些方法?
  14. Scratch 与C语言实现数据结构静态链表的建立及操作
  15. Normal模式下ASM中的空间参数解析
  16. 2022-04-01每日刷题打卡
  17. simulink模糊逻辑控制器的使用小结
  18. 【ARM-8】MPIDR_EL1, Multiprocessor Affinity Register 多处理器关联寄存器
  19. 安全运维和安全运营的区别
  20. [福大软工] Z班——Alpha现场答辩情况汇总

热门文章

  1. hyper linux v增强_Hyper-V增强会话模式 – 推酷
  2. python随机种子数_关于随机:rng种子的Python数量
  3. linux奶瓶系统,奶瓶beini 系统从硬盘光盘U盘引导启动
  4. Meta AR眼镜主管:正开发史无前例的AR,但要解决很多困难
  5. 执法记录仪,视频群呼,多方会议方案
  6. 加强班组文化建设 促进企业快速发展
  7. 转载:使用Nginx的必备软件(1.3.2)《深入理解Nginx》(陶辉)
  8. windows11创建文件夹的键盘快捷方式 - Ctrl+Shift+N
  9. 基于K210芯片的人脸识别智能门禁系统
  10. 孙明佳经验之谈有效的销售团队管理如何做?