我首先讲一下我所遭遇到的情况吧:

有n类似于下图的图片:

图像分类模型精度最高 图像分类经典算法_ide

我这里有一个需求就是,将这些图片分类,分类的效果至少也应该是这样的:

图像分类模型精度最高 图像分类经典算法_图像分类_02

将一些形状非常类似的图片分在一起.


我大致查阅了一下挺多的分类算法,这些算法大致分为了两类:

一类是通过对分类器进行训练,比如说要识别猫,那就要喂给分类器非常多的猫的图片,得到一些参数之后才能够比较精确地对图片进行识别.当然,这一大类的方法对于我们所拥有的数据来说,并不适合,我们并没有用于训练的图片.

另外一种方法不用训练,思想和机器学习里面的聚类差不太多,主要通过比较图片的一些特征,计算出与相似度类似的一个度量,来对图片进行分类.


好吧,我们大致只能使用聚类之类的东西来分类了.


究竟怎么来分呢?

说实话,我尝试了一些比较有名的聚类算法,效果都不怎么理想,然后我看到了Hausdorff Distance,这是一种在图像处理中轨迹识别什么的经常用到的距离度量,我只是抱着试一试的心态尝试了一下Hausdorff Distance加上k均值聚类,结果效果出乎我的意料,效果拔群.上面的效果你也看到了,至少在眼睛上看起来,非常不错.


关于Hausdorff Distance,推荐你看这里:

http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/98/normand/main.html

有中文翻译版的:

http://blog.sina.com.cn/s/blog_4062094e0102vkpf.html


k均值聚类应该不用我来讲了,差不多都烂了.


我这里给出Hausdorff Distance的Python代码实现吧:

"""
    目标:
    ~~~~~~~~~
    实现 Hausdorff 距离.该距离的定义如下:
    A = {a1, a2, ..., an}, B = {b1, b2, ..., bm}
    两者的 Hausdorff 距离为:
    H(A, B) = max{h(A, B), h(B, A)},
    其中
    h(A, B)的定义如下,无法用数学公式来展现,我这里用语言来描述一下得了.
    对于A中的每一个点a,首先在B中找到一个距离a最近的点b,得到a,b它们之间的距离dist(a, b),这里的dist定义如下:
    dist(a, b) = sqrt((xa - xb)^2, (ya - yb)^2),就是欧氏距离啦.
    通过计算得到n个这样的距离,找出其中最大的一个,作为h(A, B),h(B, A)同理.
"""
from math import sqrt

def euclidean_metric(pa, pb):
    '''
    计算两个点的欧式距离.
    关于输入的pa以及pb,它们应该是一个list或者tuple,长度应该相等.
    '''
    return sqrt((pa[0] - pb[0])**2 + (pa[1] - pb[1])**2)

def one_way_hausdorff_distance(sa, sb):
    '''计算单向hausdorff距离'''
    distance = 0.0
    for pa in sa: # 对于a集合中的每一个点,要求出这个点到
        shortest = 9999999
        for pb in sb:
            dis = euclidean_metric(pa, pb)
            if dis < shortest:
                shortest = dis
        if shortest > distance:
            distance = shortest
    return distance

def hausdorff_distance(sa, sb):
    '''
    计算两个集合中的点的 hausdorff 距离.
    关于输入的sa以及sb,两者应该是点的list或者tuple,而点的定义参照euclidean_metric.
    '''
    dis_a = one_way_hausdorff_distance(sa, sb)
    dis_b = one_way_hausdorff_distance(sb, sa)
    return dis_a if dis_a > dis_b else dis_b


if __name__ == '__main__':
    assert(euclidean_metric((1, 1), (1, 6)) == 5)
    A = [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)]
    B = [(0, 3), (1, 3), (2, 3), (3, 3), (3, 2), (4, 2)]
    C = [(4, 2), (4, 1), (4, 0)]
    D = [(0, 2), (1, 2), (2, 2), (2, 3), (2, 4)]
    one_way_hausdorff_distance(A, B)



另外还有一种度量公式,那就是Fréchet distance.

这是最近才找到的一种距离,将这种距离运用于分类,效果其实比上面的还要好.

它的python实现代码如下:


"""
    Fréchet distance
"""
import math
import numpy as np

# Euclidean distance.
def euc_dist(pt1,pt2):
    return math.sqrt((pt2[0]-pt1[0])*(pt2[0]-pt1[0])+(pt2[1]-pt1[1])*(pt2[1]-pt1[1]))

def _c(ca,i,j,P,Q):
    if ca[i,j] > -1:
        return ca[i,j]
    elif i == 0 and j == 0:
        ca[i,j] = euc_dist(P[0],Q[0])
    elif i > 0 and j == 0:
        ca[i,j] = max(_c(ca,i-1,0,P,Q),euc_dist(P[i],Q[0]))
    elif i == 0 and j > 0:
        ca[i,j] = max(_c(ca,0,j-1,P,Q),euc_dist(P[0],Q[j]))
    elif i > 0 and j > 0:
        ca[i,j] = max(min(_c(ca,i-1,j,P,Q),_c(ca,i-1,j-1,P,Q),_c(ca,i,j-1,P,Q)),euc_dist(P[i],Q[j]))
    else:
        ca[i,j] = float("inf")
    return ca[i,j]

""" Computes the discrete frechet distance between two polygonal lines
Algorithm: http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
P and Q are arrays of 2-element arrays (points)
"""
def frechet_distance(P,Q):
    ca = np.ones((len(P),len(Q)))
    ca = np.multiply(ca,-1)
    return _c(ca,len(P)-1,len(Q)-1,P,Q)


函数的调用方法和上面Hausdorff Distance的代码一致.