图片相似度

看了阮老师的相似图片搜索的原理有感,便用node js实现了一版,项目基于node-canvas实现,记录下canvas图形操作的一些常用api。

第一步,缩小尺寸
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

1.缩小尺寸

async function shrinkingImg (imgList=[]) {
  const list = await Promise.all(imgList.map( async item => {
    const oImg = await loadImage(imgFolder+'/'+item);
    const imgWidth = 8;
    ctx.clearRect(0, 0, imgWidth, imgWidth);
    ctx.drawImage(oImg, 0, 0, imgWidth, imgWidth);
    const data = ctx.getImageData(0,0,imgWidth,imgWidth);
    return data.data;
  }));
  return list;
}
复制代码

因为map是同步进行的加载图片是异步行为,在不使用 Promise.all 时会返回一个promise数组 如下;

[ Promise { <pending> },Promise { <pending> } ]
复制代码

2.简化色彩

通过getImageData读取图片信息,返回数组每四位为一个像素,色值如下

R - 红色 (0-255) G - 绿色 (0-255) B - 蓝色 (0-255) A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)

// index为4的整数倍索引
const newItem1 = item[index-3];
const newItem2 = item[index-2];
const newItem3 = item[index-1];

// 简化色彩转为64级灰度
const gray = (newItem1 + newItem2 + newItem3)/3;
itemList.push(~~gray);
复制代码

3.计算64级灰度的平均值

const length = arr.length;

// 计算灰度平均值
const average = arr.reduce((pre, next) => pre+next, 0)/length;
复制代码

4.比较像素的灰度计算哈希值

将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

const length = arr.length;

// 计算灰度平均值
const average = arr.reduce((pre, next) => pre+next, 0)/length;

// 计算hashreturn arr.map(item => item >= average ? 1 : 0).join('');
复制代码

通过上面的方法会返回一个包含64位长度字符串的二维数组

[[10111...,1111...],[...]]
复制代码

234步的整体代码块如下,getHashList方法参数为图片地址列表

async function getHashList (imgList) {
  const list = await shrinkingImg(imgList);
  const averageList = [];
  list.forEach(item => {
    const itemList = [];
    item.forEach((newItem, index) => {
      if ((index+1)%4 === 0) {
        const newItem1 = item[index-3];
        const newItem2 = item[index-2];
        const newItem3 = item[index-1];
        // 简化色彩转为64级灰度
        const gray = (newItem1 + newItem2 + newItem3)/3;
        itemList.push(~~gray);
      }
    }); 
    const hashData = getHash(itemList);
    averageList.push(hashData);
  });
  return averageList;
}
复制代码
function getHash (arr) {
  const length = arr.length;
  // 计算灰度平均值
  const average = arr.reduce((pre, next) => pre+next, 0)/length;
  // 计算hashreturn arr.map(item => item >= average ? 1 : 0).join('');
}
复制代码

5.对比图片hash值

编辑距离算法遍历二维数组返回相似度达到条件的图片名二维数组
getSimilarImgList方法limit参数为相似度参数,方法只会返回通过此方法处理后图片相似度达到limit的图片列表,默认值为85%。

async function getSimilarImgList (imgList=[], limit=0.85) {  
  //异常处理
  if (!imgList.length) return [];
  // 获取图片索引二维数组
  const arr = await getHashList(imgList);
  const array = [];
  // 已经匹配的图片无需再做遍历
  const includeList = [];
  for (let index = 0,length = arr.length; index < length; index++) {
    const element = arr[index];
    const list = [];
    for (let i = index+1; i < length; i++) {
      const elementNext = arr[i];
      // 获取图片相似度
      const percent = strSimilarity2Percent(element, elementNext);
      const includeItem = includeList.indexOf(i) > 0;
      if (percent>limit && !includeItem) {
        list.push(i);
        includeList.push(i);
      }
    }
    if (list.length) array.push([index,...list]);
  }
  // 按图片索引值找到对应图片名
  const mappingArr = array.map(item=>{
    return item.map(index => imgList[index]);
  });
  return mappingArr;
}
复制代码

脚本执行

node similarPicture.js [imgFolderPath] 
//imgFolderPath为要处理的包含图片文件夹  
//imgFolderPath的默认值为此脚本目录下的images
复制代码

项目源码地址: github.com/tomatoKnigh…

转载于:https://juejin.im/post/5c80be90e51d454a7c5e97ef

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
预处理:读取图片 第一步,缩小尺寸。 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。 第二步,简化色彩。 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。 第三步,计算平均值。 计算所有64个像素的灰度平均值。 第四步,比较像素的灰度。 将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。 第五步,计算哈希值。 将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。 得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hammingdistance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。 你可以将几张图片放在一起,也计算出他们的汉明距离对比,就可以看看两张图片是否相似。 这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。 实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。 以上内容大部分直接从阮一峰的网站上复制过来,想看原著的童鞋可以去在最上面的链接点击进去看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值