有一种功能叫 查找相似图片
js也可以简单的实现图片相似度识别
最终示例
体验地址 http://cdn.magiczhu.cn/index.html
代码戳这里
实现原理&步骤
- 读取本地文件 - 网络图片可以省略这一步
- 压缩图片 - 不用处理超级多的图片像素
- 图片灰度化 - 便于比较特征
- 提取特征指纹 - 取灰度的平均值 大的是1 小的是0
- 计算汉明距离 - 简单一种计算方式(还有余弦相似度等等)
- 得到相似度 - (特征字符串长度-汉明距离)/特征字符串长度
具体实现
读取本地文件
关于FileReader可以到 这里
//读取本地文件返回 src可用值
function readFile(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.addEventListener("load", function () {
resolve(reader.result);
}, false)
})
}
压缩图片
利用了canvas 当设置一个较小的宽高时会自动合并 相似的像素的特性
function compressImage(imgSrc, imgWidth = 50) {
return new Promise((resolve, reject) => {
if (!imgSrc) {
reject('imgSrc can not be empty!')
}
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.crossOrigin = 'Anonymous'
img.src = imgSrc
img.onload = function () {
canvas.width = imgWidth
canvas.height = imgWidth
ctx.drawImage(img, 0, 0, imgWidth, imgWidth)
const imageData = ctx.getImageData(0, 0, imgWidth, imgWidth)
let info = {
dataUrl: canvas.toDataURL(),
imageData,
}
resolve(info)
}
})
}
图片灰度化
灰度化,在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。
灰度化公式
//根据rgb值算出灰度值
function getGrayFromRGB(R, G, B) {
let a = Math.pow(R, 2.2) + Math.pow(1.5 * G, 2.2) + Math.pow(0.6 * B, 2.2);
let b = 1 + Math.pow(1.5, 2.2) + Math.pow(0.6, 2.2);
return parseInt(Math.pow(a / b, 1 / 2.2))
}
//根据rgba数组生成 imageData 和dataUrl
function createImageData(data) {
const canvas = document.createElement('canvas')
canvas.width = 50
canvas.height = 50
const ctx = canvas.getContext('2d')
const imgWidth = Math.sqrt(data.length / 4)
const newImageData = ctx.createImageData(imgWidth, imgWidth)
for (let i = 0; i < data.length; i += 4) {
newImageData.data[i] = data[i]
newImageData.data[i + 1] = data[i + 1]
newImageData.data[i + 2] = data[i + 2]
newImageData.data[i + 3] = data[i + 3]
}
ctx.putImageData(newImageData, 0, 0);
return {
dataUrl: canvas.toDataURL(),
imageData: newImageData
}
}
//灰度化
function grayImage(imageData) {
let data = imageData.data;
let len = imageData.data.length;
let newData = new Array(len);
for (let i = 0; i < len; i += 4) {
const R = data[i];
const G = data[i + 1];
const B = data[i + 2];
const grey = getGrayFromRGB(R, G, B);
newData[i] = grey;
newData[i + 1] = grey;
newData[i + 2] = grey;
newData[i + 3] = 255;
}
return createImageData(newData);
}
参考文章中直接使用rgb的平均值
{.is-warning}
提取特征指纹
就是这个灰度值和所有灰度值的平均值比较大的为1小的为0 连在一起的字符串
function HashFingerprint(imageData) {
const grayList = imageData.data.reduce((pre, cur, index) => {
if ((index + 1) % 4 === 0) {
pre.push(imageData.data[index - 1])
}
return pre
}, [])
const length = grayList.length
const grayAverage = grayList.reduce((pre, next) => (pre + next), 0) / length
return grayList.map(gray => (gray >= grayAverage ? 1 : 0)).join('')
}
计算汉明距离
汉明距离是以理查德·卫斯里·汉明的名字命名的。在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。例如:
1011101 与 1001001 之间的汉明距离是 2。
2143896 与 2233796 之间的汉明距离是 3。
“toned” 与 “roses” 之间的汉明距离是 3。
function getHm(str1,str2){
let distance=0;
let len = str1.length;
for(let i=0;i<len;i++){
if(str1[i]!=str2[i]){
distance++;
}
}
return distance
}
得到相似度
相似度公式:(特征字符串长度-汉明距离)/特征字符串长度
//相似度
function getSimilarity(strLen,hm){
return parseInt((strLen - hm)/strLen*100)
}
参考文章 https://mp.weixin.qq.com/s/oOlv9cbIhSwJtb-mGU8gwg
扫描二维码关注公众号,回复: 12315215 查看本文章