ar学习笔记:配准特征点的选择(代码片段)

Sakurazzy Sakurazzy     2022-12-06     482

关键词:

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

课题需要选择合适的特征点,计算相机的位姿变换矩阵,实现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 ~ 67
            switch(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);
            else
                mask_points.push_back(p1);
        
    


int main()

	Mat frame = cv::imread("front.jpg");
    Mat dst;
    // 减小图像尺寸 1200*800
    Mat 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的matrix
    dlib::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*800
    Mat 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学习笔记:边缘分割优化和提取特征点(代码片段)

AR学习笔记(五):边缘分割优化和提取特征点特征点思路1:取牙齿轮廓底部中点设置ROI及门牙中线的提取牙齿分割及边缘的提取传统方法深度学习新的提取轮廓的想法特征点计算特征点思路2:取牙齿和牙龈... 查看详情

ar学习笔记:边缘分割优化和提取特征点(代码片段)

AR学习笔记(五):边缘分割优化和提取特征点特征点思路1:取牙齿轮廓底部中点设置ROI及门牙中线的提取牙齿分割及边缘的提取传统方法深度学习新的提取轮廓的想法特征点计算特征点思路2:取牙齿和牙龈... 查看详情

ar学习笔记:边缘分割优化和提取特征点(代码片段)

AR学习笔记(五):边缘分割优化和提取特征点特征点思路1:取牙齿轮廓底部中点设置ROI及门牙中线的提取牙齿分割及边缘的提取传统方法深度学习新的提取轮廓的想法特征点计算特征点思路2:取牙齿和牙龈... 查看详情

点云配准7-特征描述子(代码片段)

...错误,大家指出。后续会补充,这个相当于自己学习笔记,便于后面复习。大篇数学公式真的是。首先我们要弄明白,什么是特征点:特征点的组成:   1.关键点:指特征点在控制里的位置(二维... 查看详情

机器学习基础理论学习笔记特征选择(featureselection)(代码片段)

...本文也许比较乱,请看目录再食用。后续会出文机器学习基础理论学习笔记(8)特征选择(featureselection)(二)将分类问题和回归问题分开总结。以及或将出文机器学习基础理论学习笔记(8)... 查看详情

自适应点云配准(ransacicp)(代码片段)

...配准,最后使用ICP的方法进行精配准。特征提取【PCL学习笔记】之快速点特征直方图FPFH-pcl::FPFHSignature33-Eba_使用FPFH( 查看详情

ar学习笔记:工具安装(代码片段)

AR学习笔记(一):工具安装mesh_image_align的安装1.OpenCV2.Eigen33.Ceres4.TheiaOpenImageIORapidJSONRockSDB遇到的问题1.makeinstall的缺少权限2.CV的函数缺失3.未声明的变量运行结果课题需要把3D模型投影到图像上,用到了github开源... 查看详情

学习loam笔记——特征点提取与匹配(代码片段)

学习LOAM笔记——特征点提取与匹配学习LOAM笔记——特征点提取与匹配1.特征点提取1.1对激光点按线束分类1.2计算激光点曲率1.3根据曲率提取特征点2.特征点匹配2.1scan-to-scan中的特征点匹配2.2scan-to-map中特征点匹配3.补充学习LOAM笔... 查看详情

学习loam笔记——特征点提取与匹配(代码片段)

学习LOAM笔记——特征点提取与匹配学习LOAM笔记——特征点提取与匹配1.特征点提取1.1对激光点按线束分类1.2计算激光点曲率1.3根据曲率提取特征点2.特征点匹配2.1scan-to-scan中的特征点匹配2.2scan-to-map中特征点匹配3.补充学习LOAM笔... 查看详情

ar学习笔记:exiv2库的编译(代码片段)

AR学习笔记(二):Exiv2库的编译exiv2(正确的食用方法)Build,Install,UseExiv2onaUNIX-likesystem(错误方式)Torunexiv2fromthebundle遇到的问题1.未定义的引用2.std::ifstreamreader不完整的类型课题需要使用到ex 查看详情

ar学习笔记:exiv2库的编译(代码片段)

AR学习笔记(二):Exiv2库的编译exiv2(正确的食用方法)Build,Install,UseExiv2onaUNIX-likesystem(错误方式)Torunexiv2fromthebundle遇到的问题1.未定义的引用2.std::ifstreamreader不完整的类型课题需要使用到ex 查看详情

使用3dslicer对图像进行配准(代码片段)

在进行深度学习之前,我们需要图像进行一些预处理操作,其中配准是很重要的一环,以下将介绍使用软件3DSlicer来进行图像配准3DSlicer是(1)一个软件平台,用以图像分析(包括配准和实时编辑),图像可视化以及图像引导治... 查看详情

详解数据预处理和特征工程-特征选择-embedded嵌入法菜菜的sklearn课堂笔记(代码片段)

...法训练同时进行。在使用嵌入法时,我们先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征。权值系数往往代表了特征对于模型的某种贡献或某种重要性,比如决策树和树的... 查看详情

pandas学习笔记一:series(代码片段)

1、series基本概念importnumpyasnpimportpandasaspd#Series数据结构#Series是带有标签的一维数组,可以保存任何数据类型(整数,字符串,浮点数,Python对象等),轴标签统称为索引ar=np.random.rand(10)print(ar,type(ar))ar 查看详情

特征选择嵌入式特征选择法(代码片段)

...载请注明出处!   嵌入式特征选择法使用机器学习模型进行特征选择。特征选择过程与学习器相关,特征选择过程与学习器训练过程融合,在学习器训练过程中自动进行特征选择。 通过L1正则化来选择特征 &nbs... 查看详情

点云配准6-pcl如何使用正态分布变换进行配准(代码片段)

...,对这边论文也进行了讲解:点云配准论文阅读笔记--3d-dnt博士论文_诺有缸的高飞鸟的博客-CSDN博客一些自己的理解(对于二维):参考:学 查看详情

学习笔记之——semi-directvisualodometry(svo)(代码片段)

本博文为本人学习SVO的学习笔记,本博文大部分内容来源于网络各种参考资料,本博文仅为本人学习记录用。目录原理Tracking(位姿估计线程)第一步:图像对齐第二步:特征对齐第三步:位姿结构优... 查看详情

4.sparkml学习笔记—sparkml决策树(应用案例)随机森林gbdt算法ml树模型参数详解(本篇概念多)(代码片段)

...机森林、GBDT算法4.1SparkML决策树4.1.1决策树定义4.1.2决策树学习过程4.1.3特征选择4.1.3.1特征选择:熵4.1.3.2特征选择:基尼4.1.3.3特征选择:方差4.1.4生成决策树的方法:ID3算法4.1.5使用决策树算法建立一个可扩展的分类器4.2集成学习4.2.1Bag... 查看详情