face-api中的人脸特征点相似度代码分析
in php前端 with 0 comment

face-api中的人脸特征点相似度代码分析

in php前端 with 0 comment

这一篇主要还是学习了下face-api是如何计算人脸特征点相似度的,给从前端计算到后端计算的移植打下了基础。之后终于可以使用PHP接管现在浏览器端的相似度计算了。

欧几里得距离

face-api中比对2张面部得特征点相似度中用到了欧几里得距离(欧氏距离),用于对比m维空间中两个点之间的真实距离。

公式如下:

微信截图_20200215222053.png

face-api中,对于欧几里得距离计算在src\euclideanDistance.ts中,具体代码如下

export function euclideanDistance(arr1: number[] | Float32Array, arr2: number[] | Float32Array) {
  if (arr1.length !== arr2.length)
    throw new Error('euclideanDistance: arr1.length !== arr2.length')

  const desc1 = Array.from(arr1)
  const desc2 = Array.from(arr2)

  return Math.sqrt(
    desc1
      .map((val, i) => val - desc2[i])
      .reduce((res, diff) => res + Math.pow(diff, 2), 0)
  )
}

因为数学不太行,从腾讯上找到一个python实现欧几里得距离计算的科普视频帮助理解:传送门

还是很简单的,用PHP写了个做测试:

<?php
$float32Array1 = [-0.13711394369602203, 0.050568852573633194, ...];
$float32Array2 = [-0.018883129581809044, 0.015689752995967865, ...];

$cumulation = 0;

for($i = 0; $i < 128; $i++) {
    $cumulation += pow($float32Array1[$i] - $float32Array2[$i], 2);
}
echo sqrt($cumulation); // 输出0.55906402065377

之后拿原版的typescript的脚本做了测试,输出为0.5590640206537717。与之相比PHP丢失了2位精度。

所以php不上BC Math拓展的话,的确不太适合做数学计算2333

FaceMatcher.findBestMatch()

查阅手册之后得知, findBestMatch函数是在src/globalApi/FaceMatcher.ts的第63行开始。

  // 这里主要还是matchDescriptor方法在对比得出最佳的描述符,跳转看下matchDescriptor
  public findBestMatch(queryDescriptor: Float32Array): FaceMatch {
    const bestMatch = this.matchDescriptor(queryDescriptor)
    return bestMatch.distance < this.distanceThreshold
      ? bestMatch
      : new FaceMatch('unknown', bestMatch.distance)
  }

  // matchDescriptor方法如下,遍历批量导入的人脸描述符,然后通过computeMeanDistance方法比对2个人脸
  public matchDescriptor(queryDescriptor: Float32Array): FaceMatch {
    return this.labeledDescriptors
      .map(({ descriptors, label }) => new FaceMatch(
          label,
          this.computeMeanDistance(queryDescriptor, descriptors)
      ))
      .reduce((best, curr) => best.distance < curr.distance ? best : curr)
  }

  // computeMeanDistance方法如下
  // 通过计算N个欧几里得距离的总和值 / N
  public computeMeanDistance(queryDescriptor: Float32Array, descriptors: Float32Array[]): number {
    return descriptors
      .map(d => euclideanDistance(d, queryDescriptor))
      .reduce((d1, d2) => d1 + d2, 0)
        / (descriptors.length || 1)
  }
Comments are closed.