在(纯)PHP / MySQL中查找相似的图像

22
我的用户正在上传图片到我的网站,我想先提供已经上传的图片。我的想法是: 1. 对每个现有的图片创建某种类型的图像“哈希值” 2. 创建新上传的图像的哈希值,并将其与数据库中的其他图像进行比较
我找到了一些有趣的解决方案,如http://www.pureftpd.org/project/libpuzzlehttp://phash.org/等,但它们存在一个或多个问题:
1. 它们需要PHP的一些非标准扩展(或根本不在PHP中)-这对我来说没问题,但我想将其创建为我的流行CMS的插件,该CMS在许多托管环境中使用而没有我的控制。 2. 它们正在比较两张图片,但我需要将一张图片与许多(例如数千张)图片进行比较,逐个比较将非常低效/慢...
我可以找到非常相似的图像(例如不同大小、重新保存的jpg或不同的jpg压缩因子),这样也没问题。
我唯一的想法是将图像调整大小为例如5px * 5px * 256种颜色,创建其字符串表示,然后找到相同的图像。但我猜即使是两个不同大小但完全相同的图像,也可能会产生微小的颜色差异,因此仅查找完全相同的图像是无用的。
因此,我需要一种良好的图像字符串表示格式,可以使用某些SQL函数来查找相似或其他不错的方法。例如,phash创建感知哈希,因此当两个数字接近时,图像也应该接近,因此我只需要查找最接近的距离。但这又是一个外部库。
有没有简单的方法?

你的想法并不差,而且256种颜色不会给你带来“微小的差异”。如果有的话,请降低这个数字。另一个重要问题是:你的图像哈希应该足够好,以处理小的图像旋转。 - madfriend
我刚想到的一个处理哈希图像旋转的想法是将哈希分成四个大小相等的部分,并旋转图像,使具有最低平均值的部分位于左下角。 - Simon Forsberg
1
pHash并不是“比较两张图片”。它为每个图像计算一个哈希值,以此来判断相似的图像是否具有相似的哈希值。然后,您可以使用特殊的数据结构来存储图像哈希,并高效地查找与上传图像的哈希类似的哈希(例如相似的图像)。 - jamix
4个回答

24

我之前遇到过完全相同的问题。

你可以尝试复制我的方法,希望它能帮到你并解决你的问题。


我是如何解决它的

我的第一个想法失败了,和你可能想的一样,我最初为每个图像(无论大小)创建了字符串。但我很快就发现这会使数据库变得非常庞大,也不太有效。

下一个可行方案是使用较小的图像(就像你的5px的想法),我用10px*10px的图像做到了这点。我为每个图像创建“哈希”的方式是使用imagecolorat()函数。

在这里查看php.net

当获取图像的RGB颜色时,我将其四舍五入到最近的50,以便颜色更少具体化。那个数字(50)是您想要根据您希望搜索的具体程度进行更改的。

例如:

// Pixel RGB
rgb(105, 126, 225) // Original
rgb(100, 150, 250) // After rounding numbers to nearest 50
将每个像素这样处理(10px*10px将给你100个rgb()),然后将它们转换为一个数组,并使用base64_encode()serialize()存储在数据库中。
当寻找相似的图像时,我对他们想要上传的图像做了完全相同的处理,然后从数据库中提取图像的“哈希值”进行比较,看哪些具有匹配的四舍五入的rgb
提示:
- 在rgb舍入中,50越大,您的搜索就越不具体(反之亦然)。 - 如果要使SQL更具体,则最好在数据库中存储关于图像的额外/特定信息,以便可以限制在数据库中获取的搜索。例如,如果宽高比为4:3,则仅从数据库中拉取周围的4:3图像。 (等) - 要精确地获得5px*5px可能很困难,建议使用phpthumb。我用的语法如下:
phpthumb.php?src=IMAGE_NAME_HERE.png&w=10&h=10&zc=1
// &w=  width of your image
// &h=  height of your image
// &zc= zoom control. 0:Keep aspect ratio, 1:Change to suit your width+height

祝你好运,伙计,希望我能帮到你。


这是一个很好的答案。与你和其他人分享的一件事是,当你将RGB值四舍五入到最近的50时,你可能会得到重复的颜色(我在许多照片中都遇到了这个问题)。通过使用PHP的array_unique()函数,这清除了所有重复项,只留下了28种颜色需要存储 - 这是一个更少的数量需要担心的问题。 - TheCarver
5
将RGB三元组四舍五入到最近的50并不正确,因此认为这样可以得到接近的颜色和相似的图像是错误的。例如124、76、76和76、76、124(带红和蓝色调),将变成100、100、100(灰色)。更好的方法是将RGB转换为一个整数(从0到16777216),然后以百或千为单位进行四舍五入。这会让你更好地接近相似的色调和颜色。 - FlamingMoe
我有一个类似的问题(想在上传之前识别重复照片),但我不明白你在这里做的事情与phash(https://github.com/jenssegers/imagehash)有什么不同?为什么这种方法更有优势?它们都会生成一个字符串,必须进行比较以找到近似重复项。 - TinyTiger

2

0

我将图像缩小到8x8,然后将RGB转换为1字节的HSV,因此结果哈希是172字节的字符串。

HSVHSVHSVHSVHSVHSVHSVHSV... (from 8x8 block, 172 bytes long)
0fff0f3ffff4373f346fff00...

它并不是100%准确的(有些重复项没有被发现),但它运行良好,看起来没有错误的结果。


0
以学术方式陈述,您要寻找的是一个相似度函数,它接受两幅图像作为输入并返回指示器,指示这两幅图像有多远/相似。该指标可以很容易地是一个从-1到1的十进制数(远到非常接近)。一旦您拥有了这个函数,就可以将一幅图像设置为参考图像,并将所有图像与其进行比较。然后,找到与某个图像相似的图像就像在像MySQL这样的RDBMS中的双字段上进行简单搜索,找到最接近的相似性因子一样简单。
现在剩下的就是如何定义相似度函数。老实说,这是问题特定的。它取决于你所谓的相似之处。但是covariance通常是一个很好的起点,它只需要您的两个图像具有相同的大小,我认为这不是什么大问题。但是您可以搜索“两个图像之间的相似度测量”来找到许多其他想法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接