名字

没想好

高度相似图片检测: 1颜色直方图差

| Comments

对于颜色直方图差的比对能很好的检测出图片之间的色差变化,那对于两张环境,光线,角度长不错的图片,能很好的检测出他们的相似度。 这是一个很老的方法,简单粗暴,非常使用。原理在这篇博客里很清楚的讲解了。 算法很简单:

  • 把两张图片缩放到统一的大小,这里可以是256x256, 并且统一成rgb模式。
  • 统计每张图片的每个颜色的出现次数。那这里因为是rgb模式,有3*256=768 种颜色,生成直方图
  • 对两张图片的直方图进行比对。如果统计结果一致的话,相似度+1

这里有个问题就是对空间信息的丢失,原作者通过把图片分割成16个小方格来解决这个问题。(split_image函数)

具体代码如下:

from PIL import Image
import sys
def normalize(img, size = (256, 256)):
    return img.resize(size).convert('RGB')

def sim_hist(hist1, hist2):
    assert len(hist1) == len(hist2)
    return sum(1 - (0 if hist1 == hist2 else float(abs(hist1 - hist2))/max(hist1, hist2)) for hist1, hist2 in zip(hist1, hist2))/len(hist1)


def calc_similar_by_path(path_img1, path_img2):
    li = normalize(Image.open(path_img1))
    ri = normalize(Image.open(path_img2))
    return sum(sim_hist(l.histogram(), r.histogram()) for l, r in zip(split_image(li), split_image(ri))) / 16.0



def split_image(img, part_size = (64, 64)):
    w, h = img.size
    pw, ph = part_size
    assert w % pw == h % ph == 0
    return [img.crop((i, j, i+pw, j+ph)).copy() \
        for i in xrange(0, w, pw) \
        for j in xrange(0, h, ph)]

if __name__ == "__main__":
    print calc_similar_by_path(sys.argv[1],sys.argv[2])

这里的fingerprint就是每个图片的直方图。 这个指纹的信息量的话应该挺大的(至少有2^768) 理论上来讲应该false positive的可能性很小,但是这个结论应该是错的,因为在图片里很多颜色是出现的概率是很小的,大部分颜色都集中于某些值中。

代码和原作者的差不多,实验结果是不错的,很简单,但是这个算法有个致命的问题就是对颜色的过分依赖。对于图片角度的变化的容忍度挺高,但是如果颜色或者图片光线变化稍大就不能很好的检测出相似度了,会把相似的图片判断为不同的。最简单的方法就是弄个灰度图,这方法基于rgb的,就无解了。还有个小问题就是,false positive,我觉得如果两张白底黑字的文字图片,不管字是多么不一样,但是他们的相似度应该很高(在有的应用上,这个结果是很希望的)

Comments