javascript原生实现图片相似度识别算法

有一种功能叫 查找相似图片
js也可以简单的实现图片相似度识别

最终示例

体验地址 http://cdn.magiczhu.cn/index.html
代码戳这里
在这里插入图片描述

实现原理&步骤

  1. 读取本地文件 - 网络图片可以省略这一步
  2. 压缩图片 - 不用处理超级多的图片像素
  3. 图片灰度化 - 便于比较特征
  4. 提取特征指纹 - 取灰度的平均值 大的是1 小的是0
  5. 计算汉明距离 - 简单一种计算方式(还有余弦相似度等等)
  6. 得到相似度 - (特征字符串长度-汉明距离)/特征字符串长度

具体实现

读取本地文件

关于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

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值