移动端电子印章解决方案
# 前言
电子印章这个概念的出现极大的方便了线下活动的进行,传统印章需要用印章粘上印泥在纸上面盖章。特别的费事,而电子印章则是通过印章在手机上盖一下,然后手机去识别完成这么一个步骤。下面来介绍一下在移动端该如何去实现这一需求
# 手机如何识别印章
其实这一步很简单,准备一个带有电容触控点的印章。将他盖在手机上,即可通过捕获touch事件来拿到这些触控点。剩下要做的就是根据这些触控点来识别是否是符合要求的图案
# 如果进行触控点之间的对比
这里提供两种方案
# 证明余弦相似
关于余弦相似度 GPT是这样解释的
余弦相似性(Cosine Similarity)是一种用于测量两个非零向量在多维空间中的夹角的相似性度量方法。这个方法常用于信息检索、自然语言处理和机器学习等领域,用于衡量文本文档、向量空间模型或特征之间的相似性。
余弦相似性的计算基于向量的内积和模的概念。对于两个非零向量A和B,它的计算公式如下:
Cosine Similarity(A, B) = (A·B) / (||A|| * ||B||)
其中:
A·B表示向量A和向量B的点积(内积)。 ||A||表示向量A的模(长度),也称为L2范数,计算方法是每个元素的平方和的平方根。 余弦相似性的结果是一个介于-1到1之间的值。具体来说:
如果余弦相似性等于1,表示两个向量的方向完全一致,相似度最高。 如果余弦相似性等于0,表示两个向量之间不存在相似性,夹角为90度。 如果余弦相似性小于0,表示两个向量之间有一定的反向相似性,夹角大于90度。
具体实现代码如下
// 计算两个向量的点积
function dotProduct(vector1, vector2) {
let dot = 0;
for (let i = 0; i < vector1.length; i++) {
dot += vector1[i] * vector2[i];
}
return dot;
}
// 计算向量的模长(范数)
function vectorLength(vector) {
let sum = 0;
for (let i = 0; i < vector.length; i++) {
sum += vector[i] * vector[i];
}
return Math.sqrt(sum);
}
// 计算余弦相似度
function cosineSimilarity(vector1, vector2) {
const dot = dotProduct(vector1, vector2);
const length1 = vectorLength(vector1);
const length2 = vectorLength(vector2);
return dot / (length1 * length2);
}
// 两个二维数组
const array1 = [[1, 2], [3, 4]];
const array2 = [[1, 2], [5, 6]];
// 将二维数组转换为一维向量
const vector1 = array1.flat();
const vector2 = array2.flat();
// 计算相似度
const similarity = cosineSimilarity(vector1, vector2);
console.log(`余弦相似度:${similarity}`);
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
27
28
29
30
31
32
33
34
35
36
37
38
39
此方法经过实测,在点位较密集的情况下准确率比较高,点位分散的情况下准确率不是很可靠。
# 计算点之间的距离
这是后面自己瞎捣鼓推测出来的方法,大概就是记录下每个点位之间的距离,通过距离判断两个二维数组是否相似。我认为在这些点里面做一个排列组合,计算出每个组合的距离基本就可以确定一个图形。所以有了如下代码
// 计算二维数组内所有坐标点之间的距离
handleCalculatedDistance(pointsArray) {
// 计算两点之间的距离
function calculateDistance(point1, point2) {
const deltaX = point1[0] - point2[0];
const deltaY = point1[1] - point2[1];
return Math.hypot(deltaX, deltaY);
}
const reslut = []
// 遍历数组并计算每对点之间的距离
for (let i = 0; i < pointsArray.length; i++) {
for (let j = i + 1; j < pointsArray.length; j++) {
const point1 = pointsArray[i];
const point2 = pointsArray[j];
const distance = calculateDistance(point1, point2);
reslut.push(distance)
}
}
return reslut
}
// 计算两组坐标的相似度
handleSimilarity(vector1, vector2) {
if (vector1.length !== vector2.length) return
// 升序排列数组
const sortVector1 = vector1.sort((a, b) => {
return a[0] - b[0];
});
const sortVector2 = vector2.sort((a, b) => {
return a[0] - b[0];
});
const reslut1 = this.handleCalculatedDistance(sortVector1).sort()
const reslut2 = this.handleCalculatedDistance(sortVector2).sort()
let exceedNum = 0 //超出接受范围外的边数量
for (let i = 0; i < reslut1.length; i++) {
console.log(Math.abs(reslut1[i] - reslut2[i]) > 10, `边距${i+1}`)
if (Math.abs(reslut1[i] - reslut2[i]) > 10) {
exceedNum++
}
}
return exceedNum === 0
}
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
提前准备一个二维数组作为对照组,然后拿到touch的触控点数组后计算出两个数组自己的排列组合,将这两个组合进行升序排列,然后进行比对。也就是用最长的和最长的比以此类推。最终如果每一条边距和对照组的误差都保持在10以内则算通过
# 结语
以上两种方案的话我各人感觉都不是太靠谱,但第二种经过本人测试基本可以解决问题并且不受方向影响,但是没法比对相似图形,如果相似的两个图形一大一小就没法比对了。