热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

对mtcnn的人脸对齐的理解

概念理解人脸识的流程:人脸检测——人脸对齐——特征提取——相似度对比人脸对齐也是关键的一步,在不同的应用场景下,会直接影响到人脸识别的

概念理解

人脸识的流程:人脸检测 ——人脸对齐——特征提取——相似度对比

人脸对齐也是关键的一步,在不同的应用场景下,会直接影响到人脸识别的结果。因为是否进行人脸对齐,会影响到提取到的特征,对齐前后提取到的特征是有差别的。

人脸对齐(矫正):就是检测到人脸角度不正,关键点不对齐,然后需要对齐操作。

人脸对齐前后的效果对比如下图,发现对齐后效果还是挺好的。

     

那么我们要怎么实施人脸对齐呢?大致的思路是:先设定一个src作为标准的人脸关键点的位置,然后和我们检测到的人脸关键点dst进行相似变换,变换的过程包括旋转、平移、缩放,这样就得到一个齐次变换矩阵M,然后把M作为参数进行仿射变换得到对齐后的人脸图片。

 

代码

可以结合下代码做更好的理解:

def preprocess(img, bbox=None, landmark=None, **kwargs):if isinstance(img, str): # 判断一个对象是否是一个已知的类型,类似type()img = read_image(img, **kwargs)M = Noneimage_size = []str_image_size = kwargs.get('image_size', '')if len(str_image_size)>0: # 得到图片的image_size,这里用112x112image_size = [int(x) for x in str_image_size.split(',')]if len(image_size)==1:image_size = [image_size[0], image_size[0]]assert len(image_size)==2assert image_size[0]==112assert image_size[0]==112 or image_size[1]==96if landmark is not None: # 如果landmark不为none,就计算出Massert len(image_size)==2src = np.array([ # 人脸的5个关键点的位置,是固定的[30.2946, 51.6963],[65.5318, 51.5014],[48.0252, 71.7366],[33.5493, 92.3655],[62.7299, 92.2041] ], dtype=np.float32 )if image_size[1]==112: # 如果为112,则要把这些坐标的横坐标都加上8.0src[:,0] += 8.0 # 那么8.0是怎么计算的呢?(112-96)/2 = 8.0dst = landmark.astype(np.float32) # 目标关键点,设置一下它的数据类型tform = trans.SimilarityTransform() # 引用 class SimilarityTransform()tform.estimate(dst, src) # 从一组对应的点估计转换M = tform.params[0:2,:] # 得到(3, 3) 的齐次变换矩阵#M = cv2.estimateRigidTransform( dst.reshape(1,5,2), src.reshape(1,5,2), False)if M is None: # 如果通过上面的变换没有找到齐次变换矩阵,就用以下的方法来调整bboxif bbox is None: #use center crop # 如果没有bbox,用中心来进行裁剪det = np.zeros(4, dtype=np.int32)det[0] = int(img.shape[1]*0.0625)det[1] = int(img.shape[0]*0.0625)det[2] = img.shape[1] - det[0]det[3] = img.shape[0] - det[1]else: # 直接使用bboxdet = bboxmargin = kwargs.get('margin', 44) # margin的值一般为0.2,表示两个类之间的间距bb = np.zeros(4, dtype=np.int32) # 4个关键点坐标bb[0] = np.maximum(det[0]-margin/2, 0)bb[1] = np.maximum(det[1]-margin/2, 0)bb[2] = np.minimum(det[2]+margin/2, img.shape[1])bb[3] = np.minimum(det[3]+margin/2, img.shape[0])ret = img[bb[1]:bb[3],bb[0]:bb[2],:] # 得到4个关键点坐标if len(image_size)>0:ret = cv2.resize(ret, (image_size[1], image_size[0])) # 图片缩放到112return ret else: #do align using landmarkassert len(image_size)==2#src = src[0:3,:]#dst = dst[0:3,:]#print(src.shape, dst.shape)#print(src)#print(dst)#print(M)warped = cv2.warpAffine(img,M,(image_size[1],image_size[0]), borderValue = 0.0) # 进行仿射变换#tform3 = trans.ProjectiveTransform()#tform3.estimate(src, dst)#warped = trans.warp(img, tform3, output_shape=_shape)return warped

 

class SimilarityTransform(EuclideanTransform):"""2D similarity transformation of the form:X = a0 * x - b0 * y + a1 == s * x * cos(rotation) - s * y * sin(rotation) + a1Y = b0 * x + a0 * y + b1 == s * x * sin(rotation) + s * y * cos(rotation) + b1where ``s`` is a scale factor and the homogeneous transformation matrix is::[[a0 b0 a1][b0 a0 b1][0 0 1]]The similarity transformation extends the Euclidean transformation with asingle scaling factor in addition to the rotation and translationparameters.Parameters----------matrix : (3, 3) array, optionalHomogeneous transformation matrix.scale : float, optionalScale factor.rotation : float, optionalRotation angle in counter-clockwise direction as radians.translation : (tx, ty) as array, list or tuple, optionalx, y translation parameters.Attributes----------params : (3, 3) arrayHomogeneous transformation matrix."""def __init__(self, matrix=None, scale=None, rotation=None,translation=None):params = any(param is not Nonefor param in (scale, rotation, translation))if params and matrix is not None:raise ValueError("You cannot specify the transformation matrix and"" the implicit parameters at the same time.")elif matrix is not None:if matrix.shape != (3, 3):raise ValueError("Invalid shape of transformation matrix.")self.params = matrixelif params:if scale is None:scale = 1if rotation is None:rotation = 0if translation is None:translation = (0, 0)self.params = np.array([[math.cos(rotation), - math.sin(rotation), 0],[math.sin(rotation), math.cos(rotation), 0],[ 0, 0, 1]])self.params[0:2, 0:2] *= scaleself.params[0:2, 2] = translationelse:# default to an identity transformself.params = np.eye(3)def estimate(self, src, dst):"""Estimate the transformation from a set of corresponding points.You can determine the over-, well- and under-determined parameterswith the total least-squares method.Number of source and destination coordinates must match.Parameters----------src : (N, 2) arraySource coordinates.dst : (N, 2) arrayDestination coordinates.Returns-------success : boolTrue, if model estimation succeeds."""self.params = _umeyama(src, dst, True)return True@propertydef scale(self):if abs(math.cos(self.rotation))

2维相似度变换公式:

\large X = a_0\cdot x - b_0\cdot y + a_1 \\ = s\cdot x\cdot \cos (rotation)-s\cdot y\cdot sin(rotation)+a_1

\large X = b_0\cdot x - a_0\cdot y + b_1 \\ = s\cdot x\cdot \sin (rotation) + s\cdot y\cdot cos(rotation)+b_1

公式中s是缩放因子,齐次变换矩阵是

[[a0  b0  a1]
 [b0  a0  b1]
 [0   0    1   ]]

参数:

matrix : (3, 3) 数组,可选的齐次变换矩阵

scale : 缩放因子

rotation : 逆时针旋转角度为弧度

translation : (tx, ty) 是一个 array, list or tuple, 转换参数

params : (3, 3) 数组,齐次变换矩阵

除了旋转和平移参数外,相似变换还扩展了具有单个比例因子的欧几里得变换。从一组相应的点估计转换,可以使用总最小二乘法确定过、好和欠的参数,且要求源坐标和目标坐标的数量必须匹配。

 


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
author-avatar
丹洋2012_757
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有