【问题标题】:What algorithm can I use to recognize the line in this scatterplot?我可以使用什么算法来识别此散点图中的线条?
【发布时间】:2013-12-16 06:09:21
【问题描述】:

我正在创建一个程序来比较音频文件,它使用与此处描述的算法相似的算法http://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf。我正在绘制被比较的两首歌曲之间的匹配时间,并为情节找到最小二乘线。 href=http://imgur.com/fGu7jhX&yOeMSK0 是匹配文件的示例图。图太乱了,即使图中有一条明显的线,最小二乘回归线也不会产生高相关系数。我可以使用其他什么算法来识别这条线?

【问题讨论】:

标签: scipy linear-regression


【解决方案1】:

这是一个有趣的问题,但一直很安静。也许这个答案 将触发更多活动。

用于识别集合中具有任意斜率和截距的线 点,霍夫变换将是一个很好的起点。为您的音频 但是,看起来斜率应该始终为 1,所以您不需要 需要 Hough 变换的全部一般性。

相反,您可以将问题视为对x - y 的差异进行聚类,其中xy 是保存点的x 和y 坐标的向量。

一种方法是计算x - y 的直方图。与斜率 1 接近位于同一直线上的点在直方图中的同一 bin 中将存在差异。具有最大计数的 bin 对应于大致对齐的最大点集合。在这种方法中要处理的一个问题是选择直方图箱的边界。错误的选择可能会导致应该组合在一起的点被拆分到相邻的 bin 中。

一种简单的蛮力方法是想象一个具有给定宽度的对角窗口,在 (x,y) 平面上从左向右滑动。一条线的最佳候选对应于包含最多点的窗口的位置。这类似于x - y 的直方图,但不是有一组不相交的 bin,而是有重叠的 bin,每个点一个。所有 bin 的宽度相同,每个点确定 bin 的左边缘。

下面代码中的函数count_diag_groups 执行该计算。对于每个点,当窗口的左边缘在该点上时,它会计算对角线窗口中有多少点。一条线的最佳候选者是具有最多点的窗口。这是脚本生成的情节。顶部是数据的散点图。底部是相同的散点图,突出显示了最佳候选点。

这个方法的一个很好的特点是只有一个参数,窗口宽度。一个不太好的特性是它的时间复杂度为 O(n**2),其中 n 是点数。肯定有时间复杂度更高的算法可以做类似的事情。您链接到的文章讨论了这一点。然而,要判断替代方案的质量,将需要更具体的规范来说明线路识别必须有多“好”或有多稳健。

import numpy as np
import matplotlib.pyplot as plt


def count_diag_groups(x, y, width):
    """
    Returns a list of arrays.  The length of the list is the same
    as the length of x.  The k-th array holds the indices into x
    (and y) of a set of points that are in a "diagonal" window with
    the given width whose left edge includes the point (x[k], y[k]).
    """
    d = x - y
    result = []
    for i in range(d.size):
        delta = d - d[i]
        neighbors = np.where((delta >= 0) & (delta <= width))[0]
        result.append(neighbors)
    return result


def generate_demo_data():
    # Generate some data.
    np.random.seed(123)
    xmin = 0
    xmax = 100
    ymin = 0
    ymax = 25
    nrnd = 175
    xrnd = xmin + (xmax - xmin)*np.random.rand(nrnd)
    yrnd = ymin + (ymax - ymin)*np.random.rand(nrnd)
    n = 25
    xx = xmin + 0.1*(xmax - xmin) + ymax*np.random.rand(n)
    yy = (xx - xx.min()) + 0.2*np.random.randn(n)
    x = np.concatenate((xrnd, xx))
    y = np.concatenate((yrnd, yy))
    return x, y


def plot_result(x, y, width, selection):
    xmin = x.min()
    xmax = x.max()
    ymin = y.min()
    ymax = y.max()

    xsel = x[selection]
    ysel = y[selection]
    # Plot...
    plt.figure(1)
    plt.clf()
    ax = plt.subplot(2,1,1)
    plt.plot(x, y, 'o', mfc='b', mec='b', alpha=0.5)
    plt.xlim(xmin - 1, xmax + 1)
    plt.ylim(ymin - 1, ymax + 1)

    plt.subplot(2,1,2, sharex=ax, sharey=ax)
    plt.plot(x, y, 'o', mfc='b', mec='b', alpha=0.5)
    plt.plot(xsel, ysel, 'o', mfc='w', mec='w')
    plt.plot(xsel, ysel, 'o', mfc='r', mec='r', alpha=0.65)
    xi = np.array([xmin, xmax])
    d = x - y
    yi1 = xi - d[imax]
    yi2 = yi1 - width
    plt.plot(xi, yi1, 'r-', alpha=0.25)
    plt.plot(xi, yi2, 'r-', alpha=0.25)
    plt.xlim(xmin - 1, xmax + 1)
    plt.ylim(ymin - 1, ymax + 1)

    plt.show()

if __name__ == "__main__":
    x, y = generate_demo_data()

    # Find a selection of points that are close to being aligned
    # with a slope of 1.
    width = 0.75
    r = count_diag_groups(x, y, width)

    # Find the largest group.
    sz = np.array(list(len(f) for f in r))
    imax = sz.argmax()
    # k holds the indices of the selected points.
    selection = r[imax]

    plot_result(x, y, width, selection)

【讨论】:

    【解决方案2】:

    这看起来像是Random Sampling Consensus (RANSAC) 的一个很好的任务示例。 维基百科的文章甚至以你的问题为例!

    大致的轮廓是这样的。

    1. 在您的数据中选择 2 个随机点,为它们拟合一条线
    2. 对于其他点,找出到该线的距离。如果距离低于阈值,则它是内点集的一部分。
    3. 如果此特定行的最终内点集大于之前的最佳行,则将新行保留为最佳候选行。
    4. 如果达到确定的迭代次数,则返回找到的最佳线,否则返回 1 并选择新的随机点。

    查看维基百科文章了解更多信息。

    【讨论】:

      猜你喜欢
      • 2010-10-06
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多