AR学习笔记:边缘分割优化和提取特征点

Posted Sakurazzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AR学习笔记:边缘分割优化和提取特征点相关的知识,希望对你有一定的参考价值。

进一步优化牙齿分割和轮廓提取的效果,做一些尝试并记录,这几天的安排大概是半天完善自己的思路,半天找找人家的方法,最难的地方是牙齿轮廓的提取


特征点思路1:取牙齿轮廓底部中点

(对于牙歪和缺牙情况不合理,放弃)

  1. 使用dlib库识别人脸特征点,取嘴唇内部轮廓点设置ROI
  2. 根据唇部特征点作出门牙中线,用于特征点的定位
  3. 进一步优化牙齿的分割,然后提取按门牙中线对称的四颗牙齿边缘轮廓
  4. 计算四颗牙齿轮廓的质心,分别作与门牙中线平行的直线,取与该牙齿轮廓相交的点作为特征点(近似为牙齿底部轮廓中点)

设置ROI及门牙中线的提取

在第三篇博客中已经完成了dlib库对人脸特征点的检测,并提取出了唇部区域,接下来连接唇部中间的特征点拟合一条直线,测试一下视频中门牙中线是否稳定,然后做一下优化

牙齿分割及边缘的提取

这个部分是最难的,在第三篇博客中做的边缘提取很粗糙,误差特别大,接下来需要尽可能把真实牙齿轮廓描出来,还是先尝试传统的图像处理方法,实在不行再用深度学习

传统方法

找到了一个github上集成了常见图像处理方法的工具:OpenCV-ToothPaint3,先以直观的方式测试一下自己的思路,过程如下

  1. 截取唇部内轮廓区域:连接轮廓特征点形成一个多边形区域,设置mask取出ROI(优化:多项式曲线拟合截取区域)
  2. 去除牙齿部分的反光:中值滤波
  3. (矫正光线:gamma矫正)
  4. 颜色分割:转换到HSV颜色空间,设置合适的阈值(目前设定为 0,0,134; 180,40,255)
  5. 锐化图像
  6. 阈值二值化
  7. 形态学处理(进行强腐蚀,检测牙齿个数,再反向膨胀循环直到牙齿个数变化,得到比较完整且分离的牙齿)
  8. 提取边缘轮廓

下图分别是提取ROI(对照),中值滤波,颜色分割的结果
中值滤波去除反光的效果很不错,颜色分割也把大部分牙龈去除了,留下比较完整的牙齿部分


下图是颜色分割(对照),锐化图像,阈值二值化的结果
锐化图像是为了是牙齿的边缘清晰,然后转化成灰度图像再进行二值化


由于当前这个工具没有集成单独的形态学处理,所以需要自己编程来实现,采用c++版本的opencv来做

深度学习

如果与牙齿分割直接相关,那么目前能找到的项目或者文献绝大部分是关于CT图像的

可能先找找增强边缘学习(Boosted Edge Learning)的文献和模型

新的提取轮廓的想法

按传统方法进行预处理后,直接通过形态学处理获取牙齿边界

特征点计算

计算四颗牙齿轮廓的质心,分别作与门牙中线平行的直线,取与该牙齿轮廓相交的点作为特征点,应该比较好实现,但稳定性和误差需要优化


特征点思路2:取牙齿和牙龈交界点

(对于牙歪和缺牙情况更为稳定)

  1. 使用dlib库识别人脸特征点,取嘴唇内部轮廓点设置ROI
  2. 根据唇部特征点作出门牙中线,用于特征点的定位
  3. 法1:分割出四颗切牙的轮廓,取每颗牙齿的顶点作为特征点,具体可以作与门牙中线的平行线,对四个轮廓逐个进行扫描(难度比较大,不太能刚好分出四个轮廓)
  4. 法2:颜色分割出牙齿的大概图像,进行边缘提取(不需要精确分割出每颗牙齿,但要求露出至少一点牙龈),取门牙中线的垂线对轮廓从上而下进行扫描,获取四个孤立的顶点

ps.如果是识别唇部内轮廓特征点,然后预估每颗牙的位置,再手动进行微调,那么对于单张图片的投影效果会很好,并且减少了自动提取特征点的麻烦。对于直播模式,不可能再手动进行微调,那么需要根据前述首张图片微调后的牙模型,以及每一帧唇部内轮廓特征点,进行一个坐标的对应,从而实现直播换牙的效果。


设置ROI及门牙中线的提取

同上先进行dlib库的人脸识别,设置唇部内轮廓线的ROI区域,并作出门牙中线,结果如下图所示

牙齿分割及边缘的提取

先使用了python来实现,方便调试
首先进行了图像预处理,包括尺寸修改,ROI设置,中值滤波,颜色分割,结果如下图所示


在这种思路下,不要求每颗牙齿的轮廓都要分开,而应尽量保持牙齿的原状减少误差,然后进行canny边缘提取,结果如下图所示

特征点计算

首先考虑提取中间两颗门牙的顶点,以门牙中线为基准,计算其斜率,在起点上方一定位置开始,作中线的垂线,并向两侧图像从上而下进行扫描,第一个交点即为该牙齿的顶点(存在多个交点的情况,需要全部检测到然后取中点)
部分代码如下所示:

# get k and k_inv  in this image : k = 52.0
    if center1[0] == center2[0]:
        k = 0
    else:
        k = (center1[1] - center2[1]) / (center1[0] - center2[0])
    k_inv = - 1 / k
    # print(k)

    # search for the first top point -- right
    point_start = (int(center1[0]+10/k), int(center1[1]-10))
    print(point_start)
    # cv2.circle(img_canny_inv, point_start, 2, (0, 255, 255))
    # cv2.imshow('img_canny_inv', img_canny_inv)
    # cv2.waitKey(0)

    get_point_flag = 0
    top_point_right = []
    count = 0

    while(get_point_flag == 0):
        count = count + 1
        point_x = int(point_start[0] - count / k)
        point_y = int(point_start[1] + count)
        for i in range(399 - point_x):
            this_point_x = int(point_x + i)
            this_point_y = int(point_y - k_inv * i)
            this_point = img_canny_inv[this_point_y, this_point_x]
            # deal with continual points
            if(this_point == 0):
                continues = 0
                while(True):
                    point_temp_x = int(this_point_x + continues)
                    point_temp_y = int(this_point_y - k_inv * continues)
                    point_temp = img_canny_inv[point_temp_y, point_temp_x]
                    if(point_temp == 0):
                        top_point_right.append([point_temp_x,point_temp_y])
                        continues = continues + 1
                    else:
                        break
                get_point_flag = 1
                break

    print(top_point_right)
    top_point_right_mid = get_mid(top_point_right)
    print(top_point_right_mid)
    # cv2.circle(img_canny_inv, top_point_right_mid, 2, (0, 255, 255))
    cv2.circle(img_canny_inv, top_point_right_mid, 2, (0, 255, 255))

检测结果如下图所示


然后考虑提取边上两颗切牙的顶点,我的想法是模仿图搜索的方式,比如提取左侧切牙的顶点,那么以左侧门牙的顶点为起点,向该像素点的上、左上、左、左下、下五个方向开始扩展,若待扩展节点像素值为0(为轮廓点),那么扩展该节点并进行下一轮的扩展,因为轮廓比较光滑,几乎不会有锯齿的情况,所以先检测一个谷底,然后再检测一个顶点,这个顶点就是左侧切牙的顶点,需要保证不会走回头路,所以在扩展节点的时候需要多判断一次(该算法是否稳定还需要更多的样本来测试)
部分代码如下所示:

    # search for the second point -- left
    print(top_point_left_mid)
    line_left_point = []
    next_point = [0,0,0,0,0]
    new_point = top_point_left_mid
    line_count = 0
    line_left_point.append(top_point_left_mid)
    while(True):
        # search only in the left
        for i in range(5):
            if  ( i == 0 ):
                next_point[i] = (new_point[0], new_point[1] - 1)
            elif( i == 1 ):
                next_point[i] = (new_point[0] - 1, new_point[1] - 1)
            elif ( i == 2 ):
                next_point[i] = (new_point[0] - 1, new_point[1])
            elif ( i == 3 ):
                next_point[i] = (new_point[0] - 1, new_point[1] + 1)
            elif ( i == 4 ):
                next_point[i] = (new_point[0], new_point[1] + 1)

        for i in range(5):
            this_point = img_canny_inv[next_point[i][1], next_point[i][0]]
            if(this_point == 0 and is_same(next_point[i],line_left_point)):
                line_left_point.append(next_point[i])
                break

        line_count = line_count + 1

        if low_flag == 0:
            point_delta_x = line_left_point[line_count][0] - line_left_point[line_count - 1][0]
            point_delta_y = line_left_point[line_count][1] - line_left_point[line_count - 1][1]

            if (point_delta_x < 0 and point_delta_y < 0):
                low_flag = 1
        else:
            point_delta_x = line_left_point[line_count][0] - line_left_point[line_count - 1][0]
            point_delta_y = line_left_point[line_count][1] - line_left_point[line_count - 1][1]

            if (point_delta_x < 0 and point_delta_y > 0):
                break

        new_point = line_left_point[-1]
        print(new_point)

得到的结果如下图所示

不同阈值下的检测效果(改变颜色分割的参数)

分割完整的情况:取阈值为[0,0,134][180,40,255],结果如下图

分割不够完整的情况:

  1. 取阈值为[0,0,134] [180,60,255],结果如下图

  2. 取阈值为[0,0,134][180,70,255],结果如下图

  3. 取阈值为[0,0,134][180,80,255],结果如下图

    分割过度的情况(牙齿部分缺损),取阈值为[0,0,134][180,30,255]

不同canny边缘提取阈值的效果

t


问题和想法

  1. 如何自动处理不同光照条件下的牙齿图像(颜色分割是否还可行)
  2. 如果用户的四颗切牙本身是歪的,那么根据门牙中线作出平行线,取与底部轮廓的交点,就不会近似牙齿底部的中点,产生很大的误差
    (考虑选择牙齿顶部的点作为特征点)
  3. 对于偏低头的照片,门牙轮廓并不位于最高的位置,所以对于思路2,在检测的时候会检测到两侧切牙的顶点
    (考虑从下向上检测顶点)
  4. 颜色分割得到的结果有时候不平滑,会出现锯齿,那么思路2找切牙顶点的时候容易找到局部顶点
    (考虑根据牙齿比例滤除掉一部分局部顶点,试试用找最外侧轮廓的方法取代canny算法)

以上是关于AR学习笔记:边缘分割优化和提取特征点的主要内容,如果未能解决你的问题,请参考以下文章

AR学习笔记:边缘分割优化和提取特征点

AR学习笔记:边缘分割优化和提取特征点

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

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

特征提取算法——Harris角点提取

AR学习笔记:阈值二值化优化与颜色分割的优化