【问题标题】:What is the best way to get the hash of a QPixmap?获取 QPixmap 哈希的最佳方法是什么?
【发布时间】:2010-10-11 11:37:22
【问题描述】:

我正在使用 Qt 4.5 开发图形应用程序并将图像放入 QPixmapCache,我想对此进行优化,以便如果用户插入已经在缓存中的图像,它将使用它。

现在每个图像都有一个唯一的 ID,这有助于优化自己的绘画事件。但是我意识到,如果我可以计算图像的哈希值,我可以查找缓存以查看它是否已经存在并使用它(当然这对重复对象更有帮助)。

我的问题是,如果它的 QPixmap 很大,它的哈希计算会减慢速度还是有更快的方法?

【问题讨论】:

  • 标题中的错字(怎么会是……):)

标签: c++ qt caching hash qpixmap


【解决方案1】:

哈希计算应该很快(如果不涉及磁盘 I/O,则在 100 MB/s 以上)取决于您使用的算法。在散列之前,您还可以进行一些快速测试以筛选出潜在的候选人 - f.e.图像必须具有相同的宽度和高度,否则比较它们的哈希值是没有用的。

当然,您还应该保留插入图像的哈希值,这样您只需为新图像计算哈希值,而不必为缓存的图像再次计算。

如果图像足够不同,可能不散列整个图像,而是散列较小的缩略图或图像的一部分(前 10 行和后 10 行),这会更快,但会导致更多碰撞。

【讨论】:

    【解决方案2】:

    我假设您是在讨论实际计算图像数据的哈希值,而不是获取 QT 生成的唯一 ID。
    根据您的图像,您可能不需要遍历整个图像来计算哈希值。也许只读取前 10 个像素?第一条扫描线?
    也许是从整个图像中随机选择像素? (使用已知种子,以便您可以重复该序列)不要忘记将图像的大小也添加到哈希中。

    【讨论】:

      【解决方案3】:

      关于这个的几个cmet:

      1. 如果您要生成像素图的哈希/缓存键,那么您可能希望跳过 QPixmapCache 并直接使用 QCache。这将消除使用 QStrings 作为键的一些开销(除非您还想使用文件路径来定位项目)

      2. 从 Qt4.4 开始,QPixmap 有一个与之关联的“散列”值(参见 QPixmap::cacheKey())。文档声称“如果不同的 QPixmap 对象引用相同的内容,它们只能具有相同的缓存键。”然而,由于 Qt 使用共享数据复制,这可能仅适用于复制的像素图,而不适用于从同一图像加载的两个不同的像素图。一点测试会告诉你它是否有效,如果有效,它会让你轻松获得哈希值。

      3. 如果您真的想要做一个良好的、相当快速的缓存并删除重复项,您可能需要查看您自己的数据结构,该结构会根据大小、颜色深度、图像类型进行排序,诸如此类。那么你只需要在找到具有相同尺寸,位深度等的相同类型的图像后,对实际图像数据进行哈希处理。当然,如果你的用户通常打开很多具有相同内容的图像,它就不会一点帮助都没有。

      4. 性能:不要忘记 Qt 在 4.5 中添加的基准测试内容,它可以让您比较各种散列方法并查看哪一个运行得最快。我还没有检查过,但它看起来很整洁。

      【讨论】:

        【解决方案4】:

        以防万一有人遇到这个问题(并且在散列事物方面不是非常有经验,尤其是像图像这样的东西),这是一个非常简单的解决方案,我用于散列 QPixmap 并将它们输入查找表以供以后比较:

        qint32 HashClass::hashPixmap(QPixmap pix)
        {
            QImage image = pix.toImage();
            qint32 hash = 0;
        
            for(int y = 0; y < image.height(); y++)
            {
                for(int x = 0; x < image.width(); x++)
                {
                    QRgb pixel = image.pixel(x,y);
        
                    hash += pixel;
                    hash += (hash << 10);
                    hash ^= (hash >> 6);
                }
            }
        
            return hash;
        }
        

        这是散列函数本身(如果您希望减少冲突,可以将其散列到 qint64 中)。如您所见,我将像素图转换为 QImage,并简单地遍历其尺寸并对每个像素执行一次非常简单的哈希并返回最终结果。有很多方法可以改进此实现(请参阅此问题的其他答案),但这是需要做的基本要点。

        OP 提到了他将如何使用此散列函数然后构造一个查找表以供以后比较图像。这需要一个非常简单的查找初始化函数——像这样:

        void HashClass::initializeImageLookupTable()
        {
            imageTable.insert(hashPixmap(QPixmap(":/Image_Path1.png")), "ImageKey1");
            imageTable.insert(hashPixmap(QPixmap(":/Image_Path2.png")), "ImageKey2");
            imageTable.insert(hashPixmap(QPixmap(":/Image_Path3.png")), "ImageKey2");
        // Etc...
        }
        

        我在这里使用了一个名为 imageTable 的 QMap,它需要在类中声明:

        QMap<qint32, QString> imageTable;
        

        然后,最后,当您想要将图像与查找表中的图像进行比较时(即:“在我知道的图像中,这是什么图像,这是特定图像吗?”),您只需调用图像上的散列函数(我假设它也将是一个 QPixmap)并且返回的 QString 值将允许您弄清楚这一点。这样的事情会起作用:

        void HashClass::compareImage(const QPixmap& pixmap)
        {
            QString value = imageTable[hashPixmap(pixmap)];
            // Do whatever needs to be done with the QString value and pixmap after this point.
        }
        

        就是这样。我希望这对某人有所帮助——它会为我节省一些时间,尽管我很高兴有解决这个问题的经验。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-04-08
          • 2012-06-23
          • 1970-01-01
          • 1970-01-01
          • 2013-03-22
          • 2021-07-30
          • 2011-01-18
          相关资源
          最近更新 更多