如何在大量无损压缩图像中提取重复的图像对(完全相同)?如何在内存中 std::hash?

Posted

技术标签:

【中文标题】如何在大量无损压缩图像中提取重复的图像对(完全相同)?如何在内存中 std::hash?【英文标题】:How to pick up repeat image pairs (exactly same) among lots of lossless compressed images ? How to std::hash in memory? 【发布时间】:2013-09-19 05:43:13 【问题描述】:

我的应用程序问题是,我可以得到大约 500 张图像,但可能有 2 张图像中的 1 或 2 张是完全相同的,这意味着文件的校验和是相同的。我的最终目标是找出哪些是重复的图像巴黎。

但是现在我必须对这 500 张图像应用压缩算法,因为未压缩的图像占用了太多磁盘空间。好吧,压缩会破坏校验和,所以我无法使用压缩图像文件的校验和来找出哪些是重复的图像对。

幸运的是,我的压缩算法是无损的,这意味着恢复的未压缩图像仍然可以以某种方式进行散列。但我只想在没有太多磁盘写访问的情况下在内存中执行此操作。所以我的问题是如何在内存中的大量图像文件中有效地提取重复图像?

我经常使用opencv,但只要它高效且不将任何文件保存在磁盘上,答案就会很好。 Python/Bash 代码也可以接受,C/C++ 和 OpenCV 是首选。

我可以考虑使用 OpenCV 的 Mat,与 std::hash,但 std::hash 不能直接工作,我必须专门编码 std::hash<cv::Mat>,我不知道该怎么做正确的呢。

我当然可以,

For each 2 images in all my images:
            if ((cv::Mat)img1 == (cv::Mat)img2):
                   print img1 and img2 are identical

但是这样效率极低,基本上是n^4算法。

注意我的问题不是图像相似度问题,而是内存中的哈希问题。

【问题讨论】:

为什么这个标签是python?但是你不能把校验和放在一个集合中,当你发现一个元素已经在集合中时删除那个图像? 我也对 python 答案感兴趣。如何有效地散列图像变量。 【参考方案1】:

获取图片哈希算法的思路:

    减小原始图像的大小(cvResize()),这样只有重要的物体会保留在图片上(去除高频)。 将图像缩小到 8x8 ,那么总像素数将是 64 并且散列将适合各种图像,无论它们的大小和纵横比如何。

    移除颜色。将上一步得到的图像转换为灰度图。 (cvCvtColor ())。因此,hash 将从 192(红、绿、蓝三个通道的 64 个值)减少到 64 个亮度值。

    求结果图像的平均亮度。 (cvAvg())

    图像的二值化。 (cvThreshold ()) 只保留那些大于平均值的像素(认为它们为 1,其他的都为 0)。

    构建哈希。将1和0图片的64个值翻译成一个64位的hash值。

接下来,如果您需要比较两个图像,那么只需为每个图像构建一个哈希并计算不同位的数量(使用汉明距离)。 汉明距离——相同长度的两个二进制字各自个数不同的位置个数。

距离为零表示很可能是同一张图片,其他值表示它们之间的差异有多大。

【讨论】:

非常感谢您的建议,但很抱歉我的问题不是图像相似度,而是散列。因为在我的图像对中,除了文件名不同外,所有像素内容都是相同的(由于传感器故障,重复图像,它不小心用相同的内存块写入了不同的文件。)。这不需要对图像进行任何预处理,而是在内存中对其进行哈希处理,这样我就不必将整个 500 张图像加载到内存中并逐个像素地比较它们。【参考方案2】:

如果它是您想要的图像的精确副本,您可以开始比较所有图像的像素 1,1,并按像素 1,1 上的相同值对它们进行分组。之后,您知道组(希望有很多组?),然后比较每个组像素 1,2 。这样,您逐个像素地进行操作,直到获得一百个左右的组为止。比你在每个组中完整地比较它们。这样,您就可以使用慢速 n^4 算法,但每次都是针对五张图片为一组,而不是一次针对 500 张图片。我假设您可以逐个像素地读取图像,我知道如果它们在 .fits 中,使用 pyfits 模块,这是可能的,但我想几乎任何图像格式都存在替代方案?

所以这背后的想法是,如果像素 1,1 不同,那么整个图像就会不同。通过这种方式,您可以制作一些可能包含前 3 个像素左右的值的列表。如果在该列表中存在足够的可变性,您可以对更小的图像组进行 1-1 完整图像检查,而不是一次检查 500 个图像。 这听起来像它应该做你想做的事吗?

【讨论】:

这是一种优化方式,但这不是我想要的,你必须在你的方法中加载/卸载文件,这需要大量的 I/O 和内存。 视情况而定,你不能逐个像素地读取它们吗?这样你就不需要阅读整个图像。我知道 .fits 可以使用 memmap 命令左右,【参考方案3】:

好的,我自己想出了一个解决方案,如果有更好的解决方案欢迎您。我把代码贴在这里。

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <openssl/md5.h>

using namespace std;
using namespace cv;

static void help()



char *str2md5(const char *str, int length) 
    int n;
    MD5_CTX c;
    unsigned char digest[16];
    char *out = (char*)malloc(33);

    MD5_Init(&c);

    while (length > 0) 
        if (length > 512) 
            MD5_Update(&c, str, 512);
         else 
            MD5_Update(&c, str, length);
        
        length -= 512;
        str += 512;
    

    MD5_Final(digest, &c);

    for (n = 0; n < 16; ++n) 
        snprintf(&(out[n*2]), 16*2, "%02x", (unsigned int)digest[n]);
    

    return out;



int main(int argc, const char** argv)

    help();

    if (argc != 2)
    
        return EXIT_FAILURE ;
    

    string inputfile = argv[1] ;

    Mat src = imread (inputfile, -1) ;

    if (src.empty())
    
        return EXIT_FAILURE ;
    



    cout << str2md5((char*)src.data, (int)src.step[0] * src.rows) << " " << inputfile << endl ;




    return 0;

您必须在您的机器上安装 OpenSSL (libssl-dev) 才能编译此代码。它将图像加载到内存中,并计算它的 md5 值。因此,要找出重复的图像对,只需编写一个简单的 bash/python 脚本,使用已编译的程序在文件的 md5 值数组中进行搜索。请注意,此 md5 检查代码不适用于大型图像文件。

【讨论】:

以上是关于如何在大量无损压缩图像中提取重复的图像对(完全相同)?如何在内存中 std::hash?的主要内容,如果未能解决你的问题,请参考以下文章

图像压缩基本matlab DCT+量化+huffman JPEG图像压缩含Matlab源码 1217期

图像的基本有损压缩和无损压缩及解压

图像压缩基于matlab余弦变换及霍夫曼编码jpeg压缩和解压含Matlab源码 2086期

图像压缩基于matlab余弦变换及霍夫曼编码jpeg压缩和解压含Matlab源码 2086期

Google 的 Page Speed 无损图像压缩是如何工作的?

在 django 上无损压缩图像