这一篇主要还是学习了下face-api是如何计算人脸特征点相似度的,给从前端计算到后端计算的移植打下了基础。之后终于可以使用PHP接管现在浏览器端的相似度计算了。
欧几里得距离
face-api中比对2张面部得特征点相似度中用到了欧几里得距离(欧氏距离),用于对比m维空间中两个点之间的真实距离。
公式如下:
![]()
face-api中,对于欧几里得距离计算在src\euclideanDistance.ts中,具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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写了个做测试:
1 2 3 4 5 6 7 8 9 10
| <?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);
|
之后拿原版的typescript的脚本做了测试,输出为0.5590640206537717。与之相比PHP丢失了2位精度。
所以php不上BC Math拓展的话,的确不太适合做数学计算2333
FaceMatcher.findBestMatch()
查阅手册之后得知, findBestMatch函数是在src/globalApi/FaceMatcher.ts的第63行开始。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public findBestMatch(queryDescriptor: Float32Array): FaceMatch { const bestMatch = this.matchDescriptor(queryDescriptor) return bestMatch.distance < this.distanceThreshold ? bestMatch : new FaceMatch('unknown', bestMatch.distance) }
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) }
public computeMeanDistance(queryDescriptor: Float32Array, descriptors: Float32Array[]): number { return descriptors .map(d => euclideanDistance(d, queryDescriptor)) .reduce((d1, d2) => d1 + d2, 0) / (descriptors.length || 1) }
|