图片相似度对比方法概述
图片相似度对比是计算机视觉中的一个重要任务,主要用于判断两张图片在内容上的相似程度。以下是几种常见的图片相似度对比方法及其实现思路:
1. 基于像素的直接比较
最直接的方法是逐像素比较两张图片的差异,常用的指标有均方误差 (MSE) 和结构相似性指数 (SSIM)。
2. 基于特征提取的方法
使用图像特征描述符 (如 SIFT、SURF 或 ORB) 提取关键点和特征向量,然后比较特征向量的相似度.
3. 基于哈希的方法
通过计算图像的哈希值 (如感知哈希 pHash、平均哈希 aHash 等) 来快速比较图片相似度。
4. 基于深度学习的方法
使用预训练的卷积神经网络 (如 ResNet、VGG 等) 提取图像特征向量,然后计算向量间的余弦相似度。
选择合适的方法
- 像素方法:简单直接,但对图像变换 (如旋转、缩放) 敏感。
- 特征方法:对图像变换有较好的鲁棒性,计算量较大。
- 哈希方法:计算速度快,适合快速筛选,但精度较低。
- 深度学习方法:精度高,对各种变化都有很好的鲁棒性,但计算资源需求大。
鸿蒙基于Hash值相似性对比
图片压缩
async imageCompressionToSize(pixelMap: image.PixelMap, targetSize: number) {
let infor = await pixelMap.getImageInfo()
if (infor.size.height > 2000) {
pixelMap.scaleSync(0.5, 0.5)
}
let value = await this.imageCompression(pixelMap, 90)
// AmLog.debugOut("imageDataSize:" + value.buffer.byteLength)
if (value.buffer.byteLength <= targetSize) {
return value
} else {
while (value.buffer.byteLength/1024 > targetSize && value.quality > 10){
try {
value = await this.imageCompression(pixelMap, value.quality - 10)
}catch (e) {
break
}
}
}
let uint = new Uint8Array(value.buffer)
let str = StrUtil.unit8ArrayToStr(uint)
let md5 = await new Md5Util().doMd(str)
value.md5 = md5
return value
}
//压缩图片
async imageCompression(pixelMap: image.PixelMap,quality:number) : Promise<ImageComValue>{
let packOption: image.PackingOption = {
format: "image/jpeg",
quality: quality
}
let packer = image.createImagePacker()
let buffer = await packer.packToData(pixelMap, packOption)
// AmLog.debugOut(quality + "压缩imageDataSize:" + buffer.byteLength)
let imageSource = image.createImageSource(buffer)
const decodeOptions:image.DecodingOptions = {
desiredPixelFormat: image.PixelMapFormat.RGBA_8888
}
let pixelMap1 = await imageSource.createPixelMap(decodeOptions)
packer.release()
return {pixMap: pixelMap1,quality,buffer}
}
计算哈希值
async getImageHash(imageSource: image.ImageSource): Promise<string> {
let packOption: image.PackingOption = {
format: "image/jpeg",
quality: 80
};
const decodeOptions:image.DecodingOptions = {
desiredSize: { width: this.imageSizeValue, height: this.imageSizeValue }, // 调整为像素大小
desiredPixelFormat: image.PixelMapFormat.RGBA_8888
}
let pixelMap = await imageSource.createPixelMap(decodeOptions)
this.imageSizeValue/imageLength)
// 转换为灰度图并计算DCT变换(此处简化处理)
// 创建图像打包器实例
const imagePackerApi = image.createImagePacker();
const pixelBuffer1:ArrayBuffer = await imagePackerApi.packToData(pixelMap,packOption)
const pixels :Uint8Array = StrUtil.bufferToUint8Array(pixelBuffer1)
//灰度计算
let grayValues:number[] = []
for (let i = 0; i < this.imageSizeValue * this.imageSizeValue; i++) {
const idx = i * 4;
if (idx + 2 >= pixels.byteLength) {
grayValues.push(0)
continue
}
// 简化的灰度计算
grayValues.push( Math.floor((pixels[idx] * 0.299) + (pixels[idx + 1] * 0.587) + (pixels[idx + 2] * 0.114)))
}
//简化版DCT(离散余弦变换)这里使用简化版DCT实现,实际应用中应使用更高效的算法
let dctPixels = this.simpleDCT(grayValues, this.imageSizeValue)
// 计算均值
const sum = dctPixels.reduce((acc, val,index) => {
// 检查是否有NaN值
if (isNaN(val)) {
// AmLog.debugOut(`发现NaN值在位置:`+ index)
return acc
}
return acc + val;
}, 0);
const mean = sum / dctPixels.length
// 生成哈希值
let hash = '';
for (let i = 0; i < dctPixels.length; i++) {
if (isNaN(dctPixels[i])) {
continue
}
hash += dctPixels[i] >= mean ? '1' : '0';
}
return hash;
}
哈希值对比
//比较图片相似度
async compareImagesByHashPath(path1: string, path2: string): Promise<number> {
const hash1 = await this.getImageHashByPath(path1);
const hash2 = await this.getImageHashByPath(path2);
// 计算汉明距离
let diff = 0;
for (let i = 0; i < hash1.length; i++) {
if (hash1[i] !== hash2[i]) diff++;
}
// 计算相似度(汉明距离越小越相似)
return (1 - diff / hash1.length) * 100;
}
//通过hash值对比图片相似度
compareImagesByHash(hash1: string, hash2: string): number {
// 计算汉明距离
let diff = 0;
for (let i = 0; i < hash1.length; i++) {
if (hash1[i] === hash2[i]) diff++;
}
// 计算相似度(汉明距离越小越相似)
return diff / hash1.length * 100;
}
性能与精度平衡
imageSizeValue的选取
哈希尺寸 | 基础尺寸 | 内存占用 | 计算时间 | 适用场景 |
---|---|---|---|---|
8×8 | 32×32 | 低 | 快 | 快速筛选 |
16×16 | 48×48 | 中 | 中 | 常规比较 |
16×64 | 96×96 | 高 | 慢 | 高精度匹配 |