寻找相似图片算法
根据阮一峰的博文实现一个js版本, 步骤如下:
- 将图片缩小到8x8的尺寸
- 转化为64灰度级
- 计算所有64个像素的灰度平均值
- 比较像素的灰度, 大于或等于平均值,记为1;小于平均值,记为0。
- 计算哈希值
更加详细的步骤参考作者原文
实现这个功能的过程中,会涉及到图片的缩放,将图片转化为灰度图, 以及生成图片hash,计算汉明距离等细节点。在实现的时候通过对canvas的操作来处理图片
图片缩放
function resize2Canvas(img, width, height) {
if (!img || !width) {
return img;
}
height = height || width;
// 按原图缩放
var detImg = img.width / img.height;
if (width / height > detImg) {
height = width / detImg;
} else {
width = height * detImg;
}
// 画到 canvas 中
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
return canvas;
}
转化图片为灰度图
转换的算法为: Y =0.299 * R + 0.587 * G + 0.114 * B
更多细节参考color-contrast
function grayscaleCanvas(canvas) {
var canvasContext = canvas.getContext('2d');
var cWidth = canvas.width;
var cHeight = canvas.height;
var canvasData = canvasContext.getImageData(0, 0, cWidth, cHeight);
var canvasDataWidth = canvasData.width;
for (var x = 0; x < cWidth; x++) {
for (var y = 0; y < cHeight; y++) {
// Index of the pixel in the array
var idx = (x + y * canvasDataWidth) * 4;
var r = canvasData.data[idx + 0];
var g = canvasData.data[idx + 1];
var b = canvasData.data[idx + 2];
// calculate gray scale value
var gray = Math.ceil((0.299 * r + 0.587 * g + 0.114 * b) / 4);
// assign gray scale value
canvasData.data[idx + 0] = gray; // Red channel
canvasData.data[idx + 1] = gray; // Green channel
canvasData.data[idx + 2] = gray; // Blue channel
canvasData.data[idx + 3] = 255; // Alpha channel
}
}
canvasContext.putImageData(canvasData, 0, 0);
return canvasData;
}
计算图片的hash值,并计算其汉明距离
/**
+ 计算图片的hash值
*/
function hash(img) {
var size = 8;
var resizedCanvas = resize2Canvas(img, size, size, false);
var canvasData = grayscaleCanvas(resizedCanvas);
var cW = canvasData.width,
cH = canvasData.height;
var totalGray = 0,
x, y, idx, grayValue;
for (x = 0; x < cW; x++) {
for (y = 0; y < cH; y++) {
// Index of the pixel in the array
idx = (x + y * cW) * 4;
grayValue = canvasData.data[idx];
totalGray += grayValue;
}
}
var meanGray = totalGray / (size * size);
var val;
var array = [];
for (x = 0; x < cW; x++) {
for (y = 0; y < cH; y++) {
// Index of the pixel in the array
idx = (x + y * cW) * 4;
grayValue = canvasData.data[idx];
if (grayValue >= meanGray) {
val = 1;
} else {
val = 0;
}
array.push(val);
}
}
return parseInt(array.join(''), 2).toString(16);
}
/**
* 计算汉明距离
*/
function hamming(h1, h2) {
var h1a = hashToBinaryArray(h1);
var h2a = hashToBinaryArray(h2);
var diff = 0;
for (var i = 0; i < h1a.length; i++) {
diff += h1a[i] ^ h2a[i];
}
return diff;
}
/**
+ 将16进制的图片hash转化为二进制
*/
function hashToBinaryArray(h) {
return parseInt(h, 16).toString(2);
}
完整的代码请查看 https://github.com/976032412/front-end-playground/tree/master/frontend/find-similar-picture
非常感谢 Andrew Zhang 的分享