基于感知哈希算法的图像搜索实现

无意中看见一篇博客,是讲仿造google搜图的,链接如下:

Google 以图搜图 - 相似图片搜索原理 - Java实现

觉得挺好玩的,博主使用Java实现的,于是我用 OpenCv实现了下。

根据看到的博文,里面说到,Google图像搜索的关键技术是“感知压缩算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个“指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。看到这里我就突然来了兴趣想自己实现!

接下来,简单介绍以下感知哈希算法:实验室邹晓艺师兄已经总结得挺好了,所以,我摘取部分:

基于低频的均值哈希

一张图片就是一个二维信号,它包含了不同频率的成分。如下图所示,亮度变化小的区域是低频成分,它描述大范围的信息。而亮度变化剧烈的区域(比如物体的边缘)就是高频的成分,它描述具体的细节。或者说高频可以提供图片详细的信息,而低频可以提供一个框架。

而一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。所以我们平时的下采样,也就是缩小图片的过程,实际上是损失高频信息的过程。如下图:

上述这些东西,我们在DSP课堂上都可以学到。

均值哈希算法主要是利用图片的低频信息,其工作过程如下:

(1)缩小尺寸:去除高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8*8的正方形。这样就可以比较任意大小的图片,摒弃不同尺寸、比例带来的图片差异。

(2)简化色彩:将8*8的小图片转换成灰度图像。

(3)计算平均值:计算所有64个像素的灰度平均值。

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

(5)计算hash值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。(我设置的是从左到右,从上到下用二进制保存)。

计算一个图片的 hash 指纹就是这么简单,计算出来的hash指纹相对比原来的图片已经丢失了太多的信息了,以至于我们都怀疑这样的指纹是不是真的能够识别出相似的图片。不过,结果当然是不用怀疑的,数学之美与编程之美的结合!

如果图片放大或缩小,或改变纵横比,结果值也不会改变。增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。这种方法是被图片的最大的优点:计算速度快!

因为就像我们看到的,一幅图片被压缩,被转为灰度图,只采集hash指纹,这个过程计算量并不大,而这些指纹就相当于图片的特征。

比较两个图片的相似性,就是先计算这两张图片的hash指纹,也就是64位0或1值,然后计算不同位的个数(汉明距离)。如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。

实际情况中,我自己写的时候,发现汉明距离小于5的要求太苛刻了,汉明距离接近20的两张图片的相似度还是挺高的。

其实看到这里,你可以根据这个思路自己去实现以下,不用看下面的代码,一直觉得这样才是学习的好方法,看博客拓展思路,自己去实现。

接下来说代码实现:

首先介绍 getImageFinger 函数,也就是生成图像的指纹,代码如下:

// 寻找图像指纹
// 参数: 输入图像 img ,图像的指纹数组
void getImageFinger(IplImage * img,char *status)
{
    int avrpixel= 0;
    int i,j;
    CvScalar scalar ;
    for (i = 0;i<8;i++)
    {
        for (j = 0;j<8;j++)
        {
            scalar = cvGet2D(img,i,j);
            avrpixel += scalar.val[0];
        }
    }
    avrpixel = avrpixel / 64 ;
    int k = 0;
    for (i = 0;i<8;i++)
    {
        for (j = 0;j<8;j++)
        {
            if (cvGet2D(img,i,j).val[0] > avrpixel)
            {
                status[k++] = 1;
            }else
            {
                status[k++] = 0;
            }
        }
    }
}

根据上面的原理就可以理解代码。

接下来是计算汉明距离的函数:

// 计算汉明距离
// 参数: 两幅图的指纹数组
// 输出: 汉明距离
int calHammDist(char * src_img,char * dst_img)
{
    int dist = 0;
    for(int i=  0;i<MAX_PIXEL_NUMBER;i++)
    {
        if (src_img[i] != dst_img[i])
        {
            dist ++;
        }
    }
    return dist;
}

搜素图片匹配的过程代码:

    // 读取本地某目录下的全部图片进行搜索匹配
    int show_number = 0;
    for (int i = 0;i<7;i++)
    {
        sprintf(FilePath,"F://image//%d.JPG",i);
        dst_img = cvLoadImage(FilePath);

        resz_dst_img = cvCreateImage(cvSize(8,8),IPL_DEPTH_8U,3);
        cvResize(dst_img,resz_dst_img,1);

        com_dst_img = cvCreateImage(cvSize(8,8),IPL_DEPTH_8U,1);
        cvCvtColor(resz_dst_img,com_dst_img,CV_RGB2GRAY);

        getImageFinger(com_dst_img,status_dst);
        dis = calHammDist(status_src,status_dst);
        cout<<" dis :"<<dis<<endl;
        if (dis<20)
        {
            showimgindex[show_number++] = i;
        }
    }

for 循环中 i 小于7 是因为我只放了7张图片,具体可以自己修改。

效果如下:

这是我本地的要搜索的7张图片

左边第一张为输入图片,第二张和第三张是匹配出来的相似度比较高的图片,第二张其实就是原来的图片,只是改变了大小尺寸,根据下面的命令行打印出来的汉明距离,第一张跟第二张的汉明距离为0,第一张和第三张的汉明距离为10,相似度还是比较高的

这里就不贴出全部代码了,实现起来不是很难,大家可以自己去试试。

时间: 2024-10-13 14:13:21

基于感知哈希算法的图像搜索实现的相关文章

Java进阶(五十七)-基于感知哈希算法的图像配准

Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法 ??毕业论文提交之后,老师交给自己一项任务:图像配准,也就是给你两幅图像,通过系统来判定两幅图像是否为同一副图像.自己作为这一方面的小白,先去网上搜索一下相应的检测方法,当然有现成的API调用最好,花钱也无所谓. ??我们这里采用的基础关键技术叫做 "感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同

三种基于感知哈希算法的相似图像检索技术

大家都用google或baidu的识图功能,上面就是我搜索一幅图片的结果,该引擎实现相似图片搜素的关键技术叫做"感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹.结果越接近,就说明图片越相似.达到图片比较目的且利用信息指纹比较有三种算法,这些算法都很易懂,下面分别介绍一下: 1.基于低频的均值哈希 一张图片就是一个二维信号,它包含了不同频率的成分.如下图

感知哈希算法

”感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个”指纹”(fingerprint)字符串,然后比较不同图片的指纹.结果越接近,就说明图片越相似. 优点:简单快速,不受图片大小缩放的影响. 缺点:图片的内容不能更改. 主要用途:根据缩略图找出原图,搜索引擎中的相似图片搜索. Hash算法原理 第一步,缩小尺寸. 将图片缩小到8×8的尺寸,总共64个像素.这一步的作用是去除图片的细节,只保留结构.明暗等基本信息,摒弃不同尺寸.比例带来的图片差异.

感知哈希算法&mdash;&mdash;找出相似的图片

Google 图片搜索功能         在谷歌图片搜索中, 用户可以上传一张图片, 谷歌显示因特网中与此图片相同或者相似的图片.         比如我上传一张照片试试效果: 原理讲解         参考Neal Krawetz博士的这篇文章, 实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似. 但关键是如何根据图片计算出"指纹&qu

感知哈希算法的java实现

一.原理讲解      实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似. 但关键是如何根据图片计算出"指纹"呢? 下面用最简单的步骤来说明一下原理:            <1>.第一步 缩小图片尺寸      将图片缩小到8x8的尺寸, 总共64个像素. 这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构

感知哈希算法(找出相似的图片)

原理讲解 实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似. 但关键是如何根据图片计算出"指纹"呢? 下面用最简单的步骤来说明一下原理: 第一步 缩小图片尺寸 将图片缩小到8x8的尺寸, 总共64个像素. 这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构.明暗等基本信息. 第二步 转为灰度图片 将缩小后的图片, 转为6

相似图片搜索的三种哈希算法

想必大家都用google或baidu的识图功能,上面就是我搜索冠希哥一幅图片的结果,达到图片比较目的且利用信息指纹比较有三种算法,这些算法都很易懂,下面分别介绍一下: 一.平均哈希算法(aHash) 此算法是基于比较灰度图每个像素与平均值来实现的,最适用于缩略图,放大图搜索. 步骤: 1.缩放图片:为了保留结构去掉细节,去除大小.横纵比的差异,把图片统一缩放到8*8,共64个像素的图片. 2.转化为灰度图:把缩放后的图片转化为256阶的灰度图. 附上灰度图相关算法(R = red, G = gr

看起来像它——图像搜索其实也不难 (图像相似,图像指纹,phash hash,图像搜索) 使用时候记得看这文章的评论

链接: http://pan.baidu.com/s/1o7ScyVo 密码: h8eb    这个文章的代码 另一个类似的代码  链接: http://pan.baidu.com/s/1hsFDCNy 密码: jxus http://blog.csdn.net/luoweifu/article/details/8220992                 使用时候记得看这文章的评论 看起来像它——图像搜索其实也不难 标签: pHash图像搜索图像识别图片搜索算法 2012-11-24 23:

就是看起来像而已——图像搜索内核探索

这是我第一次翻译外文文章,如果翻译的不好,还望大家多包含!以下黑色部分是作者原文的翻译,红色部分是我本人自己的理解和对其的补充. 原文:Looks Like It 在google里对的搜索结果是 下面是我用pHash算法(Java)实现的结果: 十张比较的图如下: source: f0a0000030400000 1-5    2-5    3-0    4-5    5-5    6-5    7-5    8-7    9-6    10-3    11-5 f0a0000030400000